PHPIndex

This page lists files in the current directory. You can view content, get download/execute commands for Wget, Curl, or PowerShell, or filter the list using wildcards (e.g., `*.sh`).

api.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/api.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerApi extends Controller{
	function __construct(){
		parent::__construct();
	}

	/**
	 * 通用文件预览方案
	 * image,media,cad,office,webodf,pdf,epub,swf,text
	 * 跨域:epub,pdf,odf,[text];  
	 * @return [type] [description]
	 */
	public function view(){
		if(!isset($this->in['path'])){
			show_tips(LNG('explorer.share.errorParam'));
		}
		$this->checkAccessToken();
		$this->setIdentify();
	}
	private function setIdentify(){
		if(!Session::get('accessPlugin')){
			Session::set('accessPlugin', 'ok');
		}
	}
	public function checkAccessToken(){
		$config = Model('Plugin')->getConfig('fileView');
		if(!$config || !$config['apiKey']){
			show_tips('fileView not open ,or apiKey is empty!');
		}

		$timeTo = isset($this->in['timeTo'])?intval($this->in['timeTo']):'';
		$token = md5($this->in['path'].$timeTo.$config['apiKey']);
		if($token != $this->in['token']){
			show_tips('token ' . LNG('common.error'));
		}
		if($timeTo != '' && $timeTo <= time()){
			show_tips('token ' . LNG('common.expired'));
		}
	}
}

attachment.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/attachment.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 通用图片附件存储模块;
 * 用于文章,日志,聊天,评论等附件上传;
 * 
 * 上传和内容关联为异步关系; 
 * 1. 附件临时存储池: 所有上层都自动存储到临时附件区域; 定期清理创建时间超过24h的临时文件;
 * 2. 存储关联对象(文章,日志,聊天,评论等): 文章或内容发布后根据id关联文档,文档转存到附件区; 
 * 3. 对象内容图片处理: 外链图片下载到本地; 图片去本地连接;
 * 4. 根据对象清理附件: 文章删除,则删除对应关联的附件资源;  meta: linkArticle=>id; linkMessage=>id;
 * 
 * 处理问题: 避免文章编辑过程中上传文件失联,没有地方可以删除(未关联的内容存储在临时存储池,定期清除;)
 * 外链图片下载到本地;域名白名单;
 */
class explorerAttachment extends Controller{
	function __construct(){
		parent::__construct();
		//去除svg; 避免xxs攻击;
		$this->imageExt = array('png','jpg','jpeg','gif','webp','bmp','ico');
	}
	
	// 上传; 扩展名限制jpg,jpeg,png,ico
	public function upload(){
		$ext = get_path_ext(Uploader::fileName());
		if(!in_array($ext,$this->imageExt)){
			show_json("only support image",false);
		}
		$this->in['name'] = date("YmdHi").rand_string(6).'.'.$ext;
		$this->in['path'] = KodIO::systemFolder('attachmentTemp/');
		Action('explorer.upload')->fileUpload();
	}
	
	// 文档评论;
	public function commentLink($id){
		$where 	= array('commentID'=>$id);
		$data 	= Model("Comment")->where($where)->find();
		if(!$data) return;
		
		$contentNew = $this->linkTarget($id,$data['content'],'comment');
		// 内容有解析变更,则替换;
		if($contentNew && $contentNew != $data['content']){
			Model("Comment")->where($where)->save(array('comment'=>$contentNew));
		}
	}
	public function commentClear($id){
		$this->clearTarget($id,'comment');
	}
	public function noticeLink($id){
		$model 	= Model("SystemNotice");
		$data 	= $model->listData($id);
		if(!$data) return;

		$contentNew = $this->linkTarget($id,$data['content'],'notice');
		// 内容有解析变更,则替换;
		if($contentNew && $contentNew != $data['content']){
			$model->update($id,array('comment'=>$contentNew));
		}
	}
	public function noticeClear($id){
		$this->clearTarget($id,'notice');
	}

	// 自动清理24小时未转移的临时文件;
	public function clearCache(){
		$timeStart  = time() - 3600*24;//1天前未关联的临时文件区域;
		$tempFolder = KodIO::systemFolder('attachmentTemp/');
		$where = array(
			'parentID' 		=> KodIO::sourceID($tempFolder),
			'createTime' 	=> array('<',$timeStart),
		);
		$sourceArr = Model("Source")->where($where)->select();
		$sourceArr = array_to_keyvalue($sourceArr,'','sourceID');
		if(!$sourceArr) return;
		foreach($sourceArr as $sourceID){
			Model('Source')->remove($sourceID,false);
		}
	}

	// 解析内容匹配图片; 关联文件到目标对象;
	public function linkTarget($id,$content,$targetType){
		$result = $this->parseImage($content);
		if(!$result['sourceArr']) return $result['content'];
		
		$metaKey 	= 'attachment_'.$targetType;
		$storePath 	= KodIO::systemFolder('attachment/'.date("Ym/d/"));
		$sourceList = Model('Source')->sourceListInfo($result['sourceArr']);
		foreach ($sourceList as $sourceInfo){
			if($sourceInfo['targetType'] != 'system') continue;
			if($sourceInfo['metaInfo'] && isset($sourceInfo['metaInfo'][$metaKey])) continue;

			IO::move($sourceInfo['path'],$storePath,REPEAT_REPLACE);
			// write_log('move from='.$sourceInfo['path'].'; to='.$storePath,'attachment');
			Model('Source')->metaSet($sourceInfo['sourceID'],$metaKey,$id);
		}
		return $result['content'];
	}

	
	// 清除目标对象关联的附件;
	public function clearTarget($id,$targetType){
		$metaKey = 'attachment_'.$targetType;
		$where 	 = array('key'=>$metaKey,'value'=>$id);
		$sourceArr = Model('io_source_meta')->where($where)->select();
		$sourceArr = array_to_keyvalue($sourceArr,'','sourceID');
		if(!$sourceArr) return true;
		
		// write_log($sourceArr,'attachment');
		foreach ($sourceArr as $sourceID){
			Model('Source')->remove($sourceID,false);
		}
	}

	// 解析图片链接到sourceID; 如果有外链需要转入的替换内容$content;
	private function parseImage($content){
		preg_match_all("/<img.*?src=[\'|\"](.*?)[\'|\"].*?[\/]?>/i",$content,$imageArr);
		if(!$imageArr) return array("sourceArr"=>false,'content'=>$content);
		$sourceArr 	= array();
		$replace 	= array();
		for($i = 0; $i < count($imageArr[1]); $i++) {
			$imageSrc 	= $imageArr[1][$i];
			$imageHtml 	= $imageArr[0][$i];
			$imageParse = $this->parseLink($imageSrc);

			if($imageParse['sourceID']) {
				$sourceArr[] = $imageParse['sourceID'];
			}
			//url解析替换;
			if($imageParse['linkNew'] && $imageSrc != $imageParse['linkNew']){
				$replace[$imageHtml] = str_replace($imageSrc,$imageParse['linkNew'],$imageHtml);
			}
		}
		$sourceArr  = array_unique($sourceArr);
		$contentNew = str_replace(array_keys($replace),array_values($replace),$content);
		return array("sourceArr"=>$sourceArr,'content'=>$contentNew);
	}
	
	private function parseLink($link){
		$local = array(
			'http://127.0.0.1/',
			'https://127.0.0.1/',
			'//127.0.0.1/',
			'http://localhost/',
			'https://localhost/',
			'//localhost/',
		);
		$linkNew = str_replace($local,'/',$link);
		if($this->proxyNeed($link)){ //外部图片链接转为内部链接;
			// $linkNew = $this->fileProxy($link);
		}
		
		preg_match("/explorer\/share\/file(&|&amp;)hash=([0-9a-zA-z_-]+)/",$link,$hash);
		$sourceID = '';
		if($hash){ // 外站kod的url自动处理;
			$pass = Model('SystemOption')->get('systemPassword');
			$path = Mcrypt::decode($hash[2],$pass);
			$ioSource = KodIO::parse($path);
			if($ioSource['type'] == KodIO::KOD_SOURCE){
				$sourceID = $ioSource['id'];
			}
		}
		return array(
			'linkNew'	=> $linkNew == $link ? '' : $linkNew,
			'sourceID'	=> $sourceID,
		);
	}

	// 外链防跨站资源; 自动远程下载;
	private function fileProxy($link){
		$saveFile = TEMP_FILES.'fileProxy_download_'.md5($link);
		$result = Downloader::start($link,$saveFile);
		if(!$result['code']) return $link;

		$ext = get_path_ext($link);
		if(!in_array($ext,$this->imageExt)){
			$ext = 'jpg';
		}
		$filename = date("YmdHi").rand_string(6).'.'.$ext;
		$storePath = KodIO::systemFolder('attachmentTemp/');
		$file = IO::move($saveFile,$storePath,REPEAT_REPLACE);
		IO::rename($file,$filename);
		return Action('explorer.share')->link($file);
	}
	private function proxyNeed($link){
		$parseUrl = parse_url($link);
		if(!$parseUrl['host']) return false;
		
		$hostAllow = array(
			'douban.com','doubanio.com',
			'qq.com',
		);
		foreach ($hostAllow as $domain){
			if(substr($parseUrl['host'],-strlen($domain)) == $domain) return true;
		}
		return false;
	}
	
}
auth.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/auth.class.php'
View Content
<?php
/*
 * @link http://kodcloud.com/
 * @author warlee | e-mail:kodcloud@qq.com
 * @copyright warlee 2014.(Shanghai)Co.,Ltd
 * @license http://kodcloud.com/tools/license/license.txt
 */

/**
 * 文档操作权限旁路拦截;
 * 
 * 如下是配置单个文档操作,统一path参数进行权限检测
 * 其他拦截:path为虚拟目录只支持列表模式 explorer.list.path;
 */
class explorerAuth extends Controller {
	private $actionPathCheck;
	function __construct() {
		parent::__construct();
		$this->isShowError = true; //检测时输出报错;
		$this->actionPathCheck = array(
			'show'		=> array("explorer.list"=>'path,listAll'),
			'view'		=> array(
				'explorer.index'=>'fileOut,fileOutBy,unzipList,pathLog,fileView,fileThumb',
				'explorer.editor' =>'fileGet'
			),
			'download'	=> array('explorer.index'=>'fileDownload,zipDownload,fileDownloadRemove'),// 下载/复制;下载/复制/文件预览打印
			'upload'	=> array(
				'explorer.upload'	=>'fileUpload,serverDownload',
				'explorer.index'	=>'mkdir,mkfile',
			),
			'edit' => array(
				'explorer.index'	=>'mkdir,mkfile,setDesc,fileSave,pathRename,pathPast,pathCopyTo,pathCuteTo,setMeta',
				'explorer.editor' 	=>'fileSave'
			),
			// 'remove'	=> array('explorer.index'=>'pathDelete'),	//批量中处理
			'share'		=> array('explorer.userShare'=>'add'),// get,edit,del 在userShare中自行判断(允许协作者(拥有者时)修改协作成员及权限)
			'comment'	=> array('comment.index'=>''),
			'event'		=> array('explorer.index'=>'pathLog'),
			'root'		=> array('explorer.index'=>'setAuth'),
		);
		
		$this->actionCheckSpace = array(//空间大小检测
			'explorer.upload'	=> 'fileUpload,serverDownload',
			'explorer.index'	=> 'mkdir,mkfile,fileSave,pathPast,pathCopyTo,pathCuteTo,unzip',
			'explorer.editor' 	=> 'fileSave',
			'explorer.share'	=> 'fileUpload',
		);
		Hook::bind('SourceModel.createBefore','explorer.auth.checkSpaceOnCreate');
	}

	// 文档操作权限统一拦截检测
	public function autoCheck(){
		$theMod 	= strtolower(MOD);
		$theAction 	= strtolower(ACTION);
		if($theMod !== 'explorer') return;

		$this->targetSpaceCheck();//空间大小检测
		Action('explorer.listGroup')->pathRootCheck($theAction);
		Action('explorer.autoPathParse')->parseAuto();
		
		// 多个请求或者包含来源去向的,分别进行权限判别;
		switch ($theAction) {//小写
			//自行判断权限,兼容文件关联附件获取信息情况;
			// case 'explorer.index.pathinfo':$this->checkAuthArray('show');break; 
			case 'explorer.index.zip':$this->checkAuthArray('edit');break;
			case 'explorer.index.zipdownload':$this->checkAuthArray('download');break;
			case 'explorer.index.unzip':
				$this->canRead($this->in['path']);
				$this->canWrite($this->in['pathTo']);
				break;
			case 'explorer.index.pathdelete':$this->checkAuthArray('remove');break;
			case 'explorer.index.pathcopy':$this->checkAuthArray('download');break;
			case 'explorer.index.pathcute':
				$this->checkAuthArray('remove');
				break;
			case 'explorer.index.pathcopyto':
				$data = json_decode($this->in['dataArr'],true);
				if(!is_array($data)){
					return $this->errorMsg('param error:dataArr!');
				}
				
				// $this->checkAuthArray('download');
				// 来源文档权限检测: 有编辑权限或者下载权限;
				$checkAction 	   = 'download'; // 没有下载权限,有复制粘贴权限时,允许从其他地方复制后粘贴(检测读取权限);
				$seleCanDownload   = Action("user.authRole")->authCanDownload();
				if(!$seleCanDownload){$checkAction = 'view';}
				
				$this->isShowError = false;$errorNum = 0;
				foreach ($data as $item) {
					$result = $this->can($item['path'],$checkAction);
					if(!$result){$errorNum++;}
				}
				$this->isShowError = true;
				if($errorNum){
					$msg  = $this->lastError ? $this->lastError : LNG('explorer.noPermissionAction');
					$code = $this->lastErrorCode ? $this->lastErrorCode : 1007;
					$this->errorMsg($msg,$code);
				}
				$this->canWrite($this->in['path']);
				break;
			case 'explorer.index.pathcuteto':
				$this->checkAuthArray('remove');
				$this->canWrite($this->in['path']);
				break;
			case 'explorer.upload.serverdownload':
			    if($this->in['path']){
			        $this->canWrite($this->in['path']);
			    }
				break;
			default:
				//直接检测;定义在actionPathCheck中的方法;参数为path,直接检测
				$actionType = $this->actionParse();
				if(isset($actionType[$theAction])){
					$authTypeArr = $actionType[$theAction];
					$errorNum = 0; // 一个控制器对应多个权限点时; 所有都失败了才算失败;一个成功就算成功;
					$this->isShowError = false;
					for ($i=0; $i < count($authTypeArr); $i++) { 
						$result = $this->can($this->in['path'],$authTypeArr[$i]);
						if(!$result){$errorNum++;}
					}
					$this->isShowError = true;
					if($errorNum == count($authTypeArr)){
						$msg  = $this->lastError ? $this->lastError : LNG('explorer.noPermissionAction');
						$code = $this->lastErrorCode ? $this->lastErrorCode : 1007;
						$this->errorMsg($msg,$code);
					}
				}
				break;
		}
	}
	
	public function targetSpaceCheck(){
		$actions = array();
		foreach ($this->actionCheckSpace as $controller => $stActions) {
			$stActions = explode(',',trim($stActions,','));
			foreach ($stActions as $action) {
				$actions[] = strtolower($controller.'.'.$action);
			}
		}
		if(!in_array(strtolower(ACTION),$actions)) return;
		if(!$this->spaceAllow($this->in['path'])){
			show_json(LNG('explorer.spaceIsFull'),false);
		}
	}
	public function spaceAllow($path){
		$parse  = KodIO::parse($path);
		if($parse['isTruePath'] != true) return true;
		if($parse['driverType'] == 'io') return true;
		
		$info = IO::infoAuth($parse['pathBase']);//目标存储;
		// 目标在回收站中: 不支持保存/上传/远程下载/粘贴/移动到此/新建文件/新建文件夹;
		$fromDav = _get($GLOBALS,'requestFrom') == 'webdav';
		if($info['isDelete'] == '1' && !$fromDav){
			$msg = $info['type'] == 'file' ? LNG('explorer.pathInRecycleFile') : LNG("explorer.pathInRecycle");
			show_json($msg,false);
		}
		$space  = Action("explorer.list")->targetSpace($info);
		if(!$space || $space['sizeMax']==0 ) return true; // 没有大小信息,或上限为0则放过;
		$result = $space['sizeMax'] > $space['sizeUse'];
		if(!$result){$this->lastError = LNG('explorer.spaceIsFull');}
		return $result;
	}

	public function checkSpaceOnCreate($sourceInfo){
		$space = false;
		if($sourceInfo['targetType'] == SourceModel::TYPE_GROUP){
			$space = $this->space('group',$sourceInfo['targetID']);
		}else if($sourceInfo['targetType'] == SourceModel::TYPE_USER){
			$space = $this->space('user',$sourceInfo['targetID']);
		}
		
		if(!$space || $space['sizeMax']==0 ) return true; // 没有大小信息,或上限为0则放过;
		if($space['sizeMax']  <= $space['sizeUse']+ $sourceInfo['size'] ){
			show_json(LNG('explorer.spaceIsFull'),false);
		}
	}
	
	// 用户空间占用集中处理;
	public function space($targetType,$targetID){
		if($targetType == 'user'){//空间追加处理;
			$target = Model('User')->getInfo($targetID);
		}else{
			$target = Model('Group')->getInfo($targetID);
		}
		$target['sizeUse'] = (!$target['sizeUse'] || $target['sizeUse'] <= 0 ) ? 0 : intval($target['sizeUse']);
		$result = array(
			'targetType'	=> $targetType,
			'targetID' 		=> $targetID,
			'targetName'	=> $target['name'],
			"sizeMax" 		=> floatval($target['sizeMax'])*1024*1024*1024,
    		"sizeUse" 		=> $target['sizeUse'],
		);
		$result = Hook::filter("explorer.targetSpace",$result);
		return $result;
	}
	
	
	// 外部获取文件读写权限; Action("explorer.auth")->fileCanRead($path);
	public function fileCanRead($file){
		if($this->fileIsShareLink($file)){return $this->fileCan($file,'view');}
		if(!ActionCall('user.authRole.authCanRead')) return false;
		return $this->fileCan($file,'view');
	}
	public function fileCanDownload($file){
		if($this->fileIsShareLink($file)){return $this->fileCan($file,'download');}
		if(!ActionCall('user.authRole.authCanRead')) return false;
		return $this->fileCan($file,'download');
	}
	public function fileCanWrite($file){
		if($this->fileIsShareLink($file)){return $this->fileCan($file,'edit');}
		if(!ActionCall('user.authRole.authCanEdit')) return false;
		return $this->fileCan($file,'edit');
	}
	private function fileIsShareLink($file){
		return strpos($file,'{shareItemLink:') === 0 ? true:false;
	}
	
	public function fileCan($file,$action){
		if(request_url_safe($file)) return true;
		$this->isShowError = false;
		$result = $this->can($file,$action);
		$this->isShowError = true;
		return $result;
	}

	/**
	 * 检测文档权限,是否支持$action动作
	 * 目录解析:拦截只支持列目录,但当前方法为其他操作的
	 * 获取目录信息:自己的文档,则放过
	 * 
	 * 权限判断:
	 * 1. 是不是:是不是自己的文档;是的话则跳过权限检测
	 * 2. 能不能:
	 * 		a. 是我所在的部门的文档:则通过权限&动作判别
	 * 		b. 是内部协作分享给我的:检测分享信息,通过权限&动作判别
	 * 		c. 其他情况:一律做权限拦截;
	 * 
	 * 操作屏蔽:remove不支持根目录:用户根目录,部门根目录,分享根目录;
	 */
	public function can($path,$action){
		if(!$path || !is_string($path)){return false;}
		$isRoot = KodUser::isRoot();
		$parse  = KodIO::parse($path);
		$ioType = $parse['type'];
		
		if($ioType == KodIO::KOD_SHARE_LINK){
			$pathInfo	= Action('explorer.share')->sharePathInfo($path);
			$shareInfo	= Action('explorer.share')->shareInfoLast();
			if ($pathInfo && $shareInfo) {
				// if(in_array($action,array('view','show'))) return true;
				if($action == 'show') return true;
				if($action == 'view' && _get($shareInfo,'options.notView') !='1' ) return true;
				if($action == 'download' && _get($shareInfo,'options.notDownload') !='1' ) return true;
				if($action == 'edit' && _get($shareInfo,'options.canEditSave') == '1' ){
					if(Model('SystemOption')->get('shareLinkAllowEdit') == '0'){ // 全局开关;
						return $this->errorMsg(LNG('explorer.pathNotSupport'),1108);
					}
					return true;
				}
			}
			return $this->errorMsg(LNG('explorer.pathNotSupport'),1108);
		}
		if(!$isRoot && !Action('user.authRole')->canCheckRole($action)){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1021);
		}
		if($ioType == KodIO::KOD_SHARE_OUTER){ // 外部分享内容; 权限在内容自行处理;
			$driver = IO::init($path);
			if($driver->allowAction($action)){return true;}
			return $this->errorMsg(IO::getLastError(LNG('explorer.noPermissionAction')),1021);
		}
		
		// 物理路径 io路径拦截;只有管理员且开启了访问才能做相关操作;
		if( $ioType == KodIO::KOD_IO || $ioType == false ){
			if( request_url_safe($path) ) return $action == 'view';
			
			// 允许管理存储,即可访问系统回收站及挂载的存储.
			$pathCanRead = Action('user.authRole')->authCan('admin.storage.edit');
			if($pathCanRead && $this->config["ADMIN_ALLOW_IO"]) return true;
			if($pathCanRead && $parse['id'] == 'systemRecycle') return true;
			return $this->errorMsg(LNG('explorer.pathNotSupport'),1001);
		}
		
		//个人挂载目录;跨空间移动复制根据身份处理;
		if( $ioType == KodIO::KOD_USER_DRIVER ) return true;
		
		//不支持删除自己的桌面
		if($action == 'remove'){
			if(trim($path,'/') == trim(MY_DESKTOP,'/')){
				return $this->errorMsg(LNG('explorer.desktopDelError'),1100);
			}
			if(trim($path,'/') == trim(MY_HOME,'/')){
				return $this->errorMsg(LNG('explorer.pathNotSupport'),1100);
			}
		}
		
		// 纯虚拟路径只能列表; 不支持其他任何操作;
		if( $this->pathOnlyShow($path) ){
			if($action == 'show') return true;
			return $this->errorMsg(LNG('explorer.pathNotSupport'),1002);
		}

		//分享内容;分享子文档所属分享判别,操作分享权限判别;
		if( $ioType == KodIO::KOD_SHARE_ITEM){
			return $this->checkShare($parse['id'],trim($parse['param'],'/'),$action);
		}

		$pathInfo = IO::infoAuth($parse['pathBase']);
		Hook::trigger("explorer.auth.can",$pathInfo,$action);
		// 个人私密空间是否登录检测;
		if($pathInfo && isset($pathInfo['sourceID']) && $pathInfo['targetType'] == 'user'){
			$errorMsg = Action('explorer.listSafe')->authCheck($pathInfo,$action);
			if($errorMsg){return $this->errorMsg($errorMsg,1101);}
		}
		
		//上层文件夹是否需要密码;
		if($pathInfo && isset($pathInfo['sourceID'])){
			$errorMsg = Action('explorer.listPassword')->authCheck($pathInfo,$action);
			if($errorMsg){return $this->errorMsg($errorMsg,1102);}
		}
		
		// source 类型; 新建文件夹 {source:10}/新建文件夹; 去除
		//文档类型检测:屏蔽用户和部门之外的类型;
		if($this->allowRootSourceInfo($pathInfo)) return true;
		$targetType = $pathInfo['targetType'];
		// if(!$pathInfo) return true; 
		if(!$pathInfo){//不存在,不判断文档权限;
			return $this->errorMsg(LNG('common.pathNotExists'),0);
		}
		if( $targetType != 'user' && $targetType != 'group' ){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1003);
		}
		
		//个人文档;不属于自己
		if( $targetType == 'user' && $pathInfo['targetID'] != USER_ID ){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1004);
		}

		//部门文档:权限拦截;会自动匹配权限;我在的部门会有对应权限
		if($targetType == 'group'){
			$auth  = $pathInfo['auth']['authValue'];
			return $this->checkAuthMethod($auth,$action);
		}
		// 删除操作:拦截根文件夹;用户根文件夹,部门根文件夹
		if( $pathInfo['parentID'] == '0' && $action=='remove' ){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1100);
		}
		return true;
	}
	
	public function allowRootSourceInfo($pathInfo){
		$isRoot = KodUser::isRoot();
		if($isRoot && $this->config["ADMIN_ALLOW_SOURCE"]) return true;
		if($isRoot && $pathInfo && $pathInfo['pathType'] == '{systemRecycle}') return true;
		return false;
	}

	// 路径类型中: 检测目录是否可操作(属性,重命名,新建上传等); 纯虚拟路径只能列表;
	public function pathOnlyShow($path){
		$parse  = KodIO::parse($path);
		$truePath = array( // 可以操作的目录类型;
			KodIO::KOD_IO,
			KodIO::KOD_SOURCE,
			KodIO::KOD_SHARE_ITEM,
		);
		$canAction = in_array($parse['type'],$truePath);
		return $canAction? false:true;
	}
	
	private function errorMsg($msg,$code=false){
		if($this->isShowError){
			return show_json($msg,false,$code);	
		}
		$this->lastError 	 = $msg;
		$this->lastErrorCode = $code;
		return false;
	}
	public function getLastError(){return $this->lastError;}
	public function canView($path){return $this->can($path,'view');}
	public function canRead($path){return $this->can($path,'download');}
	public function canWrite($path){return $this->can($path,'edit');}
	private function checkAuthArray($action){
		$data = json_decode($this->in['dataArr'],true);
		if(!is_array($data)){
			return $this->errorMsg('param error:dataArr!');
		}
		foreach ($data as $item) {
			$this->can($item['path'],$action);
		}
	}
	
	/**
	 * 根据权限值判别是否允许该动作
	 * $method: view,show,...   AuthModel::authCheckShow...
	 */
	private function checkAuthMethod($auth,$method){
		if(KodUser::isRoot() && $this->config["ADMIN_ALLOW_SOURCE"]) return true;
		$auth = intval($auth);
		if(!$auth || $auth == 0){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1005);
		}

		//某文档有权限,打通上层文件夹通路;
		if($method == 'show' && $auth === -1) return true;
				
		$method   = strtoupper(substr($method,0,1)).substr($method,1);
		$method   = 'authCheck'.$method;
		$allow = Model('Auth')->$method($auth);
		if(!$allow){
			return $this->errorMsg(LNG('explorer.noPermissionAction'),1006);
		}
		return true;
	}
	
	/**
	 * 分享检测:
	 * 1. 分享存在;文档存在;
	 * 2. 文档属于该分享或该分享的子目录
	 * 2. 且自己在分享目标中; 权限不等于0 说明自己在该分享中;
	 * 3. 权限动作检测
	 * 
	 * 分享根文件夹不支持删除操作;
	 */
	public function checkShare($shareID,$sourceID,$method){
		$shareInfo = Model('Share')->getInfoAuth($shareID);
		$sharePath = $shareInfo['sourceID'].'';
		if(!$shareInfo || !$shareInfo['sourceInfo'] ){
			return $this->errorMsg(LNG('explorer.share.notExist'));
		}
		if( $sharePath == $sourceID && $method =='remove' ){
			return $this->errorMsg("source share root can't remove !");
		}
		
		// 自己协作分享的内容; 权限同自己拥有的权限;
		if($shareInfo['userID'] == USER_ID){
			$sourceInfo = $shareInfo['sourceInfo'];
			if( $sourceInfo['targetType'] == 'user' && $sourceInfo['targetID'] == USER_ID ){
				return Action('user.authRole')->canCheckRole($method);
			}
			if(!$shareInfo['sourceInfo']['auth']){ // 自己内部协作分享 io路径(拥有后台存储管理权限的角色)
				return Action('explorer.authUser')->can($shareInfo['sourcePath'],$method,$shareInfo['userID']);
			}
			return $this->checkAuthMethod($shareInfo['sourceInfo']['auth']['authValue'],$method);
		}

		// 分享时间处理;
		$timeout = intval(_get($shareInfo,'options.shareToTimeout',0));
		if($timeout > 0 && $timeout < time()){
			return $this->errorMsg(LNG('explorer.share.expiredTips'));
		}
		
		// 关闭协作分享的;
		if($shareInfo['isShareTo'] == '0'){
			return $this->errorMsg(LNG('explorer.share.notExist').'[status=0]');
		}

		// 内部协作分享有效性处理: 当分享者被禁用,没有分享权限,所在文件不再拥有分享权限时自动禁用外链分享;
		if(!Action('explorer.authUser')->canShare($shareInfo)){
			$userInfo = Model('User')->getInfoSimpleOuter($shareInfo['userID']);
			$tips = '('.$userInfo['name'].' - '.LNG('common.noPermission').')';
			return $this->errorMsg(LNG('explorer.share.notExist').$tips);
		}
		
		// 物理路径,io路径;
		if($shareInfo['sourceID'] == '0'){
			$sharePath = KodIO::clear($shareInfo['sourcePath']);
			$thisPath  = KodIO::clear($shareInfo['sourcePath'].$sourceID);
			if(substr($thisPath,0,strlen($sharePath)) != $sharePath) return false;
			return $this->checkAuthMethod($shareInfo['auth']['authValue'],$method);
		}
		
		if (!$sourceID) $sourceID = $shareInfo['sourceID'];
		$sourceInfo = Model('Source')->sourceInfo($sourceID);
		$parent = Model('Source')->parentLevelArray($sourceInfo['parentLevel']);
		array_push($parent,$sourceID);
		
		if(!$sourceInfo || !in_array($sourceID,$parent) ){
			return $this->errorMsg(LNG('explorer.share.notExist'));
		}
		// 自己的分享,不判断权限;协作中添加了自己或自己所在的部门;
		if( $sourceInfo['targetType'] == SourceModel::TYPE_USER && 
			$sourceInfo['targetID'] == USER_ID ){
			return true;
		}
		return $this->checkAuthMethod($shareInfo['auth']['authValue'],$method);
	}
	
	//解析上述配置到action列表;统一转为小写;
	private function actionParse(){
		$actionArray = array();
		foreach ($this->actionPathCheck as $authType => $modelActions) {
			foreach ($modelActions as $controller => $stActions) {
				if(!$stActions) continue;
				$stActions = explode(',',trim($stActions,','));
				foreach ($stActions as $action) {
					$fullAction = strtolower($controller.'.'.$action);
					if(!isset($actionArray[$fullAction])){
						$actionArray[$fullAction] = array();
					}
					$actionArray[$fullAction][] = $authType;
				}
			}
		}
		// pr($actionArray,$this->actionPathCheck);exit;
		return $actionArray;
	}
}
authUser.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/authUser.class.php'
View Content
<?php
/*
 * @link http://kodcloud.com/
 * @author warlee | e-mail:kodcloud@qq.com
 * @copyright warlee 2014.(Shanghai)Co.,Ltd
 * @license http://kodcloud.com/tools/license/license.txt
 */

/**
 * 检测某人对某文档操作权限
 */
class explorerAuthUser extends Controller {
	function __construct() {
		parent::__construct();
	}

	/**
	 * 检测文档权限,是否支持$action动作
	 * path: 支持物理路径/io路径/source路径; (不支持其他路径)
	 * action: view,download,upload,edit,remove,share,comment,event,root
	 */
	public function can($path,$action,$userID){
		$roleInfo = Action('user.authRole')->userRoleGet($userID);
		if(!$roleInfo) return false;
		$isRoot = $roleInfo['info']['administrator'] == 1;
		if(!$isRoot && !Action('user.authRole')->canCheckRole($action)){return false;}
		
		$parse  = KodIO::parse($path);$ioType = $parse['type'];
		// 物理路径 io路径拦截;只有管理员且开启了访问才能做相关操作;
		if( $ioType == KodIO::KOD_IO || $ioType == false ){
			$allowIO = $isRoot || ($roleInfo['allowAction']['admin.storage.edit'] == 1);
			if($allowIO && $this->config["ADMIN_ALLOW_IO"]) return true;
			return false;
		}
		
		if($isRoot && $this->config["ADMIN_ALLOW_SOURCE"]) return true;
		$pathInfo = Model('Source')->pathInfo($parse['id']);
		$targetType = $pathInfo['targetType'];
		if(!$pathInfo || $pathInfo['isDelete'] == '1') return false;//不存在,不判断文档权限;
		if( $targetType != 'user' && $targetType != 'group' ) return false;// 不是个人或部门文档
		if( $targetType == 'user' && $pathInfo['targetID'] != $userID ) return false; //个人文档但不是自己的文档

		//部门文档:权限拦截;会自动匹配权限;我在的部门会有对应权限
		if($targetType == 'group'){
			$selfAuth  = $this->makeUserAuth($userID,$parse['id']);
			if(!$selfAuth || !Model("Auth")->authCheckAction($selfAuth['authValue'],$action)) return false;
		}
		return true;
	}
	
	public function canShare($shareInfo){
		if(!$shareInfo) return false;
		// 兼容早期版本,该字段为空的情况;
		if(!$shareInfo['sourcePath'] && $shareInfo['sourceID'] != '0'){
			$shareInfo['sourcePath'] = KodIO::make($shareInfo['sourceID']);
		}
		if($shareInfo['isLink'] == '0' && $shareInfo['isShareTo'] == '0'){return false;}
		if($shareInfo['userID'] == '0'){return true;} // 系统分享,允许访问;
		
		// 系统分享;则不检测;
		$isSystemSource 	= '/systemPath/systemSource/';
		$pathDisplay 		= _get($shareInfo,'sourceInfo.pathDisplay');
		$isSystem 			= _get($shareInfo,'sourceInfo.targetType') == 'system';
		if(substr($pathDisplay,0,strlen($isSystemSource)) == $isSystemSource && $isSystem) return true;
		return $this->can($shareInfo['sourcePath'],'share',$shareInfo['userID']);
	}
	
	public function makeUserAuth($userID,$sourceID){
		$pathInfo  = Model('Source')->pathInfo($sourceID);
		$authList  = Model("SourceAuth")->getSourceList(array($sourceID),false,$userID);
		if( $authList && isset($authList[$sourceID])) return $authList[$sourceID];
		return Action('explorer.listGroup')->pathGroupAuthMake($pathInfo['targetID'],$userID);
	}
}
autoPathParse.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/autoPathParse.class.php'
View Content
<?php

// source 路径自动解析补全处理; {source:xx1}/aa/bb/c;  ==> 转为{source:xx2}
class explorerAutoPathParse extends Controller {
	function __construct() {
		parent::__construct();
	}

	// 入口统一处理;
	public function parseAuto(){
		$theAction 	= strtolower(ACTION);
		$checkArr   = array(
			'explorer.index.pathinfo' 		=> array('path','[dataArr]'),
			'explorer.index.pathrename'		=> array('path'),
			'explorer.index.pathdelete'		=> array('[dataArr]'),
			'explorer.index.pathcopy'		=> array('[dataArr]'),
			'explorer.index.pathcute'		=> array('[dataArr]'),
			'explorer.index.pathcopyto'		=> array('path','[dataArr]'),
			'explorer.index.pathcuteto' 	=> array('path','[dataArr]'),
			'explorer.index.mkdir' 			=> array('path'),
			'explorer.index.mkfile' 		=> array('path'),
			'explorer.index.filesave' 		=> array('path'),
			'explorer.index.setmeta' 		=> array('path'),
			'explorer.index.setdesc' 		=> array('path'),
			'explorer.index.setauth' 		=> array('path'),
			'explorer.index.fileout' 		=> array('path'),
			'explorer.index.filedownload' 	=> array('path'),
			'explorer.index.zipdownload' 	=> array('path'),
			'explorer.index.filedownloadremove' => array('path'),
			'explorer.index.fileoutby' 		=> array('path'),
			'explorer.index.unzip' 			=> array('path','pathTo'),
			'explorer.index.unziplist' 		=> array('path'),
			'explorer.index.pathlog' 		=> array('path'),
			'explorer.index.filethumb' 		=> array('path'),
			'explorer.list.path'	 		=> array('path'),
			'explorer.list.listall'	 		=> array('path'),
			'explorer.usershare.add'	 	=> array('path'),
			'explorer.upload.fileupload'	=> array('path'),
			'explorer.upload.serverdownload'=> array('path'),
			'explorer.editor.fileget'		=> array('path'),
			'explorer.editor.filesave'		=> array('path'),
		);
		if(!$checkArr[$theAction]){return;}
		
		$allowNotMatchAction = array(
			'explorer.index.mkdir' 		=> 1,
			'explorer.index.mkfile' 	=> 1,
			'explorer.index.unzip' 		=> 1,
  		);
		$allowNotMatch  = isset($allowNotMatchAction[$theAction]) ? true : false;
		foreach($checkArr[$theAction] as $key){
			if(substr($key,0,1) == '['){
				$this->parseArr(substr($key,1,-1),$allowNotMatch);
			}else{
				$this->parseKey($key,$allowNotMatch);
			}
		}
		// pr($this->in,$_REQUEST);exit;
	}
	
	public function parseKey($key,$allowNotMatch=false){
		if(!$this->in[$key]){return;}
		$this->in[$key] = $this->parsePath($this->in[$key],$allowNotMatch);
	}
	public function parseArr($key,$allowNotMatch=false){
		$data = json_decode($this->in[$key],true);
		if(!is_array($data)){return;}
		foreach($data as $k=>$v){
			if(!is_array($v) || !isset($v['path'])){continue;}
			$data[$k]['path'] = $this->parsePath($v['path'],$allowNotMatch);
		}
		$this->in[$key] = json_encode($data);
	}
	
	public function parsePath($path,$allowNotMatch=false){
		if(!$path){return $path;}
		if(substr($path,0,8) == '{search}'){
			return $this->parseSourceSearch($path);
		}
		
		if(substr($path,0,8) != '{source:'){return $path;}
		$path = KodIO::clear($path);
		$info = preg_match("/^(\{source:(\d+)\})(\/?.*)$/",$path,$match);
		if(!$match || !trim($match[3],'/')){return $path;}
		
		$pathArr  = explode('/',trim($match[3],'/'));
		$sourceID = $match[2];
		foreach($pathArr as $i=>$name){
			$pathInfo = $this->parseSource($sourceID,$name);
			if(!$pathInfo){ // 不存在时, 当前文档为文件时; 直接返回;  允许文件路径后续追加无效内容;
				$itemInfo = Model("Source")->field("sourceID,name,isFolder")->where(array('sourceID'=>$sourceID,'isDelete'=>0))->find();
				if($itemInfo && $itemInfo['isFolder'] == '0'){$pathInfo = $itemInfo;}
			}
			if($pathInfo && $pathInfo['isFolder'] == '0'){// 如果是文件则直接返回;忽略后续内容;
				$sourceID = $pathInfo['sourceID'];break;
			}
			
			if($pathInfo){$sourceID = $pathInfo['sourceID'];continue;}
			if(!$allowNotMatch){return show_json(LNG('common.pathNotExists'),false);}
			return '{source:'.$sourceID.'}/'.implode('/',array_slice($pathArr,$i));
		}
		return '{source:'.$sourceID.'}/';
	}
	private function parseSourceSearch($path){
		if(substr($path,0,8) != '{search}'){return $path;}
		if(!strstr($path,'parentPath=')){return $path;}
		$all    = explode('@',ltrim(substr($path,8),'/'));
		$result = array();
		foreach ($all as $item) {
			$keyv = explode('=',$item ? $item : '');
			if(count($keyv) == 2 || $keyv[0] == 'parentPath'){
				$value = trim(rawurldecode($keyv[1]));
				$value = $this->parsePath($value);
				$item = $keyv[0].'='.rawurlencode($value);
			}
			$result[] = $item;
		}
		return '{search}/'.implode('@',$result);
	}
	
	private function parseSource($sourceID,$name){
		static $cache = array();
		$cacheKey = $sourceID.'-'.$name;
		if(!trim($name)){return $sourceID;}
		if(array_key_exists($cacheKey,$cache)){
			return $cache[$cacheKey];
		}
		$where    = array('parentID'=>$sourceID,'name'=>$name,'isDelete'=>0);
		$pathInfo = Model("Source")->field("sourceID,name,isFolder")->where($where)->find();
		$cache[$cacheKey] = $pathInfo ? $pathInfo : 0;
		return $cache[$cacheKey];
	}
}
editor.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/editor.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerEditor extends Controller{
	function __construct()    {
		parent::__construct();
	}
	
	public function fileGet(){
		$path = Input::get('path','require');
		$this->fileGetHistoryCheck($path);
		$this->fileGetZipContentCheck($path);
		if(request_url_safe($path)){
			$driver   = new PathDriverUrl();
			return $this->fileGetMake($path,$driver->info($path),$path);
		}
		$pathInfo = Action('explorer.index')->pathInfoItem($path);
		Action('explorer.index')->updateLastOpen($path);
		$this->fileGetMake($path,$pathInfo);
	}
	
	// 历史版本文件获取;
	private function fileGetHistoryCheck($path){
		if(!request_url_safe($path)) return;
		$urlInfo = parse_url_query($path);
		if(!isset($urlInfo['explorer/history/fileOut']) || !isset($urlInfo['path'])) return;
		
		$this->in['path'] = rawurldecode($urlInfo['path']);
		$this->in['id']   = $urlInfo['id'];
		$info = IO::info($this->in['path']);
		if(!$info) return;
		
		$action = isset($info['sourceID']) ? 'explorer.history':'explorer.historyLocal';
		$pathInfo = Action($action)->fileInfo();// 内部做权限检测;
		if(!$pathInfo){return show_json(LNG('common.pathNotExists'),false);}
		$this->fileGetMake($pathInfo['path'],$pathInfo,$path);exit;
	}
	
	private function contentPage($path,$size){
		// if($size >= 1024*1024*20){show_json(LNG('explorer.editor.fileTooBig'),false);}
		$PAGE_MIN 	= 1024 * 100;
		$PAGE_MAX 	= $GLOBALS['config']['settings']['ioReadMax'];
		$pageNum 	= _get($this->in,'pageNum',1024 * 500);
		$pageNum	= $pageNum <= $PAGE_MIN ? $PAGE_MIN : ($pageNum >= $PAGE_MAX ? $PAGE_MAX : $pageNum);
		$pageNumOld = $pageNum;
		if($pageNum >= $size){$pageNum = $size;}

		$pageTotal	= $pageNum > 0 ? ceil($size/$pageNum):0;
		$page		= _get($this->in,'page',1);
		$page		= $page <= 1 ? 1  : ($page >= $pageTotal ? $pageTotal : $page);
		$from = 0;$length = $size;
		if($size > $PAGE_MIN){
			$from = ($page - 1) * $pageNum;
			$length = $pageNum;
		}
		
		if(request_url_safe($path)){
			$driver  = new PathDriverUrl();
			$content = $driver->fileSubstr($path,$from,$length);
		}else{
			$content = IO::fileSubstr($path,$from,$length);
		}
		
		if($content === false){
			show_json(IO::getLastError(LNG('explorer.error')),false);
		}
		if($size == 0){$content = '';}
		return array(
			'content' 	=> $content,
			'pageInfo'	=> array(
				'page'		=> $page,
				'pageNum'	=> $pageNumOld,
				'pageTotal'	=> $pageTotal,
				'totalNum'	=> $size
			)
		);
	}
	
	// 压缩包内文件请求优化; 请求内部文件链接识别并处理成直接访问
	private function fileGetZipContentCheck($path){
		if(!request_url_safe($path)) return;
		$urlInfo = parse_url_query($path);
		if(!isset($urlInfo['index']) || !isset($urlInfo['path']) || !isset($urlInfo['accessToken'])) return;
		if(!Action('user.index')->accessTokenCheck($urlInfo['accessToken'])){return;}
		
		$zipFile  = rawurldecode($urlInfo['path']);
		$indexArr = @json_decode(rawurldecode($urlInfo['index']),true);
		if(!$indexArr || !$zipFile){show_json(LNG('common.pathNotExists'),false);}
		
		$isShareFile = isset($urlInfo['explorer/share/unzipList']);
		$isViewFile  = isset($urlInfo['explorer/index/unzipList']);
		// 权限判断;
		if($isShareFile){
			$shareFileInfo = Action("explorer.share")->sharePathInfo($zipFile);
			if(!$shareFileInfo){show_json(LNG('explorer.noPermissionAction'),false);}
			$zipFile = $shareFileInfo['path'];
		}else if($isViewFile){
			if(!Action('explorer.auth')->canRead($zipFile)){
				show_json(LNG('explorer.noPermissionAction'),false);
			}
		}else{return;}
		
		// 解压处理;
		$filePart  = IOArchive::unzipPart($zipFile,$indexArr);
		if(!$filePart || !IO::exist($filePart['file'])){
			show_json(LNG('common.pathNotExists'),false);
		}
		$this->fileGetMake($filePart['file'],IO::info($filePart['file']),$path);exit;
	}
	
	public function fileGetMake($path,$pathInfo,$pathUrl = false){
		// pr($path,$pathInfo,$pathUrl);exit;
		if($pathUrl){ //url类文件信息处理; 只读, pathUrl,编辑器刷新支持;
			$urlInfo = parse_url_query($pathUrl);
			$showPath = trim(rawurldecode($urlInfo['name']),'/');
			if(isset($urlInfo['index']) && $urlInfo['index']){ // 压缩包内部文件;
				$indexArr = json_decode(rawurldecode($urlInfo['index']),true);
				if(is_array($indexArr)){$showPath = $indexArr[count($indexArr) - 1]['name'];}
			}
			$pathInfo['path'] 	= '';$pathInfo['pathUrl'] = $pathUrl;
			$pathInfo['name'] 	= get_path_this($showPath);
			$pathInfo['ext'] 	= get_path_ext($pathInfo['name']);
			$pathInfo['pathDisplay'] = "[".$showPath."]";
			$pathInfo['isWriteable'] = false;
		}
		if(!$pathInfo || $pathInfo['type'] == 'folder'){
			return show_json(LNG('common.pathNotExists'),false);
		}
		Hook::trigger('explorer.fileGet', $path);
		$contentInfo = $this->contentPage($path,$pathInfo['size']);
		$content 	 = $contentInfo['content'];
		if(isset($this->in['charset']) && $this->in['charset']){
			$charset = strtolower($this->in['charset']);
		}else{
			$charset = strtolower(get_charset($content));
		}
		
		//ISO-8859-1 // 0x00 ~ 0xff;不做转换 1字节全; byte类型;  避免编码转换导致内容变化的情况: \xC3\x86 => \xC6;
		$isBindary = strstr($content,"\x00") ? true:false;//pr($isBindary,$content);exit;
		if($isBindary){$this->in['base64'] = 1;}
		if($isBindary && !isset($this->in['charset'])){$charset = 'iso-8859-1';}
		if(!$isBindary && $charset !='' && $charset !='utf-8' && function_exists("mb_convert_encoding")){
			$content = @mb_convert_encoding($content,'utf-8',$charset);
		}
		
		$data = array_merge($pathInfo,array(
			'charset'		=> $charset,
			'base64'		=> $this->in['base64'] == '1' ?'1':'0',// 部分防火墙编辑文件误判问题处理
			'pageInfo' 		=> $contentInfo['pageInfo'],
			'content'		=> $content,
		));
		
		// 避免截取后乱码情况; 去掉不完整的字符(一个汉字3字节,最后剩余1,2字节都会导致乱码)
		if(!$isBindary && is_text_file($pathInfo['ext']) && $data['pageInfo']['pageTotal'] > 1){
			$data['content'] = utf8Repair($data['content'],chr(1));
		}
		if($data['base64']=='1'){
			$data['content'] = strrev(base64_encode($data['content']));
		}
		show_json($data);
	}
	
	public function fileSave(){
		$data = Input::getArray(array(
			'path'			=> array('check'=>'require'),
			'content'		=> array('default'=>''),
			'base64'		=> array('default'=>''),
			'charset'		=> array('default'=>''),
			'charsetSave' 	=> array('default'=>''),
		));
		$pathInfo = IO::info($data['path']);
		if(!$pathInfo) show_json(LNG('common.pathNotExists'),false);
		
		//支持二进制文件读写操作(base64方式)
		$content = $data['content'];
		if($data['base64'] == '1'){
			$content = base64_decode(strrev($content));//避免防火墙拦截部分关键字内容
		}
		$charset 	 = strtolower($data['charset']);
		$charsetSave = strtolower($data['charsetSave']);
		$charset  	 = $charsetSave ? $charsetSave : $charset;
		if ( $charset !='' && 
			 $charset != 'utf-8' && 
			 $charset != 'ascii' &&
			 function_exists("mb_convert_encoding")
			) {
			$content = @mb_convert_encoding($content,$charset,'utf-8');
		}
		$result = IO::setContent($data['path'],$content);
		$msg = $result ? LNG("explorer.saveSuccess") : IO::getLastError(LNG('explorer.saveError'));
		$pathInfo = Action('explorer.index')->pathInfoItem($data['path']);
		show_json($msg,!!$result,$pathInfo);
	}
	
	/*
	* 保存编辑器配置信息
	*/
	public function setConfig(){
		$optionKey = array_keys($this->config['editorDefault']);
		$data = Input::getArray(array(
			"key"	=> array("check"=>"in","param"=>$optionKey),
			"value"	=> array("check"=>"require"),
		));
		Model('UserOption')->set($data['key'],$data['value'],'editor');
		show_json(LNG('explorer.settingSuccess'));
	}	
}
fav.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/fav.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerFav extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  = Model('UserFav');
	}
	/**
	 * 获取收藏夹json
	 */
	public function get() {
		$pageNum = $GLOBALS['in']['pageNum'];$GLOBALS['in']['pageNum'] = 2000;//分页处理;
		$list = $this->model->listView();
		
		// 收藏协作分享内容;
		foreach($list as $key => $item) {
			$pathParse = KodIO::parse($item['path']);
			if($pathParse['type'] == KodIO::KOD_SHARE_ITEM){
				$infoItem = IO::info($item['path']);
				$infoItem = is_array($infoItem) ? $infoItem :array();
				$list[$key] = array_merge($item,$infoItem);	
			}
		}
		
		$GLOBALS['in']['pageNum'] = $pageNum ? $pageNum:null;
		return $this->_checkExists($list);
	}
	
	private function _checkExists($list){
		foreach ($list as &$item) {
			if(!isset($item['sourceInfo'])){
				$item['sourceInfo'] = array();
			}
			$item['sourceInfo']['isFav']   = 1;
			if(!$item['sourceInfo']['favName']){
				$item['sourceInfo']['favName'] = $item['name'];
			}
			if(!$item['sourceInfo']['favID']){
				$item['sourceInfo']['favID'] = $item['id'];
			}
			unset($item['id']);
			if( $item['type'] == 'source' && $item['sourceID']){
				$item['type'] = $item['isFolder'] == '1' ? 'folder':'file';
				$item['path'] = KodIO::make($item['path']);
				continue;
			}
			if( $item['type'] == 'source' ){
				// 文件不存在处理;
				$item['type']   = 'folder';
				$item['exists'] = false;
				$item['path']   = KodIO::make($item['path']);
			}else{
				$info = Action('explorer.list')->pathCurrent($item['path'],false);
				if($item['type'] == 'file'){$info['type'] = 'file';}
				unset($info['name']);
				$item = array_merge($item,$info);
				if($item['type'] == 'file'){$item['ext'] = get_path_ext($item['name']);}
			}
		};unset($item);
		return $list;
	}
	public function favAppendItem(&$item){
		static $listPathMap = false;
		if($listPathMap === false){
			$listPathMap = $this->model->listData();
			$listPathMap = array_to_keyvalue($listPathMap,'path');
		}
		
		if(!isset($item['sourceInfo'])){$item['sourceInfo'] = array();}
		$path 	 = $item['path'];$path1 = rtrim($item['path'],'/');$path2 = rtrim($item['path'],'/').'/';
		$findItem = isset($listPathMap[$path]) ? $listPathMap[$path]:false;
		$findItem = (!$findItem && isset($listPathMap[$path1])) ? $listPathMap[$path1]:$findItem;
		$findItem = (!$findItem && isset($listPathMap[$path2])) ? $listPathMap[$path2]:$findItem;
		if($findItem){
			$item['sourceInfo']['isFav'] = 1;
			$item['sourceInfo']['favName'] = $findItem['name'];
			$item['sourceInfo']['favID'] = $findItem['id'];
		}
		if($item['type'] == 'file' && !$item['ext']){
			$item['ext'] = get_path_ext($item['name']);
		}
		return $item;
	}

	/**
	 * 添加
	 */
	public function add(){
		$data = Input::getArray(array(
			"path"	=> array("check"=>"require"),
			"name"	=> array("check"=>"require"),
			"type"	=> array("check"=>"require","default"=>'folder'),
		));
		$list = $this->model->listData();
		$list = is_array($list) ? $list : array();
		if( count($list) > $GLOBALS['config']['systemOption']['favNumberMax'] ){
			show_json(LNG("common.numberLimit"),false);
		}
		
		$pathInfo = KodIO::parse($data['path']);
		if($pathInfo['type'] == KodIO::KOD_USER_FAV){
			//show_json(LNG("explorer.pathNotSupport"),false);
		}
		if($pathInfo['type'] == KodIO::KOD_SOURCE){
			$data['type'] = 'source';
			$data['path'] = $pathInfo['id'];
			Action('explorer.listSafe')->authCheckAllow($data['path']);
		}
		$res = $this->model->addFav($data['path'],$data['name'],$data['type']);
		$msg = !!$res ? LNG('explorer.addFavSuccess') : LNG('explorer.pathHasFaved');
		show_json($msg,!!$res);
	}

	/**
	 * 重命名
	 */
	public function rename() {
		$data = Input::getArray(array(
			"name"		=> array("check"=>"require"),
			"newName"	=> array("check"=>"require"),
			"path"		=> array("check"=>"require","default"=>false),
		));
		$res = $this->model->rename($data['name'],$data['newName']);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.repeatError');
		$info = $res && $data['path'] ? $data['path']:false;
		show_json($msg,!!$res,$data['path']);
	}
	
	/**
	 * 置顶
	 */
	public function moveTop() {
		$name = Input::get('name','require');
		$res = $this->model->moveTop($name);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}

	/**
	 * 置底
	 */
	public function moveBottom() {
		$name = Input::get('name','require');
		$res = $this->model->moveBottom($name);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}

	/**
	 * 重置排序,根据id的顺序重排;
	 */
	public function resetSort() {
		$idList = Input::get('favList',"require");
		$idArray = explode(',',$idList);
		if(!$idArray) {
			show_json(LNG('explorer.error'),false);
		}
		$res = $this->model->resetSort($idArray);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}	

	/**
	 * 删除
	 */
	public function del() {
		$name = Input::get('name','require');
		$res = $this->model->removeByName($name);
		$msg = !!$res ? LNG('explorer.delFavSuccess') : LNG('explorer.error');
		show_json($msg,!!$res);
	}
}
fileView.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/fileView.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerFileView extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	public function index(){
		$file = $this->in['path'];	
		$fileInfo = IO::info($file);
		$app = $this->getAppUser($fileInfo['ext']); 
		$fileUri = rawurlencode($file);

		if(!$app){
			return header('Location: '.APP_HOST.'#fileView&path='.$fileUri);
		}
		$object = Action($app.'Plugin');
		$existMethod = method_exists($object,'index');	
		if(!$existMethod){
			return header('Location: '.APP_HOST.'#fileView&path='.$fileUri);
		}		
		
		$link = urlApi('plugin/'.$app,'path='.$fileUri);
		$link .= '&ext='.rawurlencode($fileInfo['ext']).'&name='.rawurlencode($fileInfo['name']);
		header('Location: '.$link);
	}
	
	// 根据当前用户打开方式;
	private function getAppUser($ext){
		$list = $this->getAppSupport($ext,true);
		if(!$list) return false;
		$listName   = array_to_keyvalue($list,'name');
		$defaultSet = Model('UserOption')->get('kodAppDefault');
		$defaultSet = json_decode(Model('UserOption')->get('kodAppDefault'),true);

		$app = $list[0]['name'];
		if( isset($defaultSet[$ext]) && 
			isset($listName[$defaultSet[$ext]]) ){
			$app = $listName[$defaultSet[$ext]]['name'];
		}
		return $app;
	}

	// 打开方式获取并排序;
	private function getAppSupport($ext,$checkUserAuth = true){
		$modelPlugin = Model("Plugin");
		$list = $modelPlugin->loadList();
		$listAllow = array();
		foreach ($list as $app => $item) {
			if( !isset($item['config']['fileExt']) ) continue;
			$extArr = explode(',',$item['config']['fileExt']);
			if( !in_array($ext,$extArr) ) continue;
			
			if( $modelPlugin->appAllow($app,$item,$checkUserAuth) ){
				$item['fileOpenSort'] = intval($item['config']['fileSort']);
				$listAllow[] = $item;
			}
		}
		$listAllow = array_sort_by($listAllow,'fileOpenSort',true);
		return $listAllow;
	}
}
history.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/history.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerHistory extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model = Model('SourceHistory');
		$this->checkAuth();
	}

	private function checkAuth(){
		$path = Input::get('path','require');
		$info = IO::info($path);
		if( !isset($info['sourceID']) ){ // 物理目录,io路径代理请求;
			Action('explorer.historyLocal')->delegate();exit;
		}
		Action('explorer.auth')->canWrite($path);
		$this->sourceID = $info['sourceID'];
	}
	private function checkItem(){
		$id = Input::get('id','require');
		$where = array('sourceID' =>$this->sourceID,'id'=> $id);
		$itemInfo = $this->model->where($where)->find();
		if(!$itemInfo){ // 没有匹配到该文档的某条历史记录;
			show_json(LNG('explorer.dataError'),false);
		}
		return $id;
	}

	/**
	 * 获取历史记录列表;
	 */
	public function get() {
		$result = $this->model->listData($this->sourceID);
		$result['current'] = IO::info($this->in['path']);
		show_json($result);
	}
	
	/**
	 * 删除某个文件的某个版本;
	 */
	public function remove() {
		$id  = $this->checkItem();
		$res = $this->model->removeItem($id);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}
	
	/**
	 * 删除某个文件的所有版本;
	 */
	public function clear() {
		$res = $this->model->removeBySource($this->sourceID);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}
	
	//设置某个版本为最新版;
	public function rollback() {
		$id  = $this->checkItem();
		$fileInfo = $this->model->fileInfo($id);
		Model("Source")->checkLock($this->sourceID,$fileInfo['fileID']);
		$res = $this->model->rollbackToItem($this->sourceID,$id);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}
	public function setDetail(){
		$maxLength = $GLOBALS['config']['systemOption']['historyDescLengthMax'];
		$msg  = LNG('common.lengthLimit').'('.LNG('explorer.noMoreThan').$maxLength.')';
		$data = Input::getArray(array(
			'detail'=> array('check'=>'length','param'=>array(0,$maxLength),'msg'=>$msg),
		));
		
		$id  = $this->checkItem();
		$res = $this->model->setDetail($id,$data['detail']);
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res);
	}
	public function fileOut(){
		$fileInfo = $this->fileInfo();
		$isDownload = isset($this->in['download']) && $this->in['download'] == 1;
		Hook::trigger('explorer.fileOut', $fileInfo['path']);
		if(isset($this->in['type']) && $this->in['type'] == 'image'){
			return IO::fileOutImage($fileInfo['path'],$this->in['width']);
		}
		$name = !empty($this->in['name']) ? $this->in['name'] : $fileInfo['name'];
		IO::fileOut($fileInfo['path'],$isDownload,$name);
	}
	public function fileInfo(){
		$id  = $this->checkItem();
		return $this->model->fileInfo($id);
	}
}
historyLocal.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/historyLocal.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 本地文件,io文件历史记录
 * 
 * 
 * 历史版本生成
 * 操作触发检测: 文件编辑保存;上传覆盖;粘贴覆盖;(排除历史版本回退)  IO::setContent
 * 目录变更更新: 重命名,上层文件夹重命名;移动,上层文件夹移动;
 * 删除处理: 文件删除,上层文件夹删除;
 * 历史版本管理: 列表,打开,下载;回退,删除,清空; diff对比
 */
class explorerHistoryLocal extends Controller{
	function __construct(){
		parent::__construct();
		$this->checkAuth();
	}
	
	public function checkAuth(){
		$path = Input::get('path','require');
		Action('explorer.auth')->canWrite($path);
		$this->path = $this->parsePath($path);
		if(!$this->path){
			show_json(LNG('explorer.dataError'),false);
		}
	}
	public function delegate(){
		$action = ACT;
		$this->$action();
	}
	
	// 目录检测;
	private function parsePath($path){
		if(!$path) return false;
		$pathParse  = KodIO::parse($path);
		$driverType = $pathParse['type'];
		$allow = !$driverType || $driverType == KodIO::KOD_IO || $driverType == KodIO::KOD_SHARE_ITEM;
		if(!$allow || !$pathParse['isTruePath']) return false;
		
		if($driverType == KodIO::KOD_SHARE_ITEM){
			$driver = IO::init($path);
			if($driver->pathParse['truePath']){
				return $this->parsePath($driver->pathParse['truePath']);
			}
			return false;
		}
		return $path;
	}
	
	public function get(){
		$history = IOHistory::listData($this->path);
		if(!$history){show_json(LNG('explorer.dataError'),false);}
		$result   = array_page_split($history['list']);

		if(Model('SystemOption')->get('versionType') == 'A'){
			$result['list'] = array_slice($history['list'],0,5);
			$result['pageInfo']['totalNum']  = count($result['list']);
			$result['pageInfo']['pageTotal'] = 1;
			$result['pageInfo']['page'] = 1;
		}
		$userList = array();
		foreach ($result['list'] as $key=>$item){
			$userKey = 'user-'.$item['createUser'];
			if(!$userList[$userKey]){
				$userList[$userKey] = Model("User")->getInfoSimpleOuter($item['createUser']);
			}
			$result['list'][$key]['createUser'] = $userList[$userKey];
		}
		$result['current'] = IO::info($this->in['path']);
		show_json($result);
	}
	public function remove(){
		$res = IOHistory::remove($this->path,$this->in['id']);
		$msg = $res ? LNG('explorer.success') : IO::getLastError(LNG('explorer.error'));
		show_json($msg,!!$res);
	}
	public function clear(){
		$res = IOHistory::clear($this->path);
		$msg = $res ? LNG('explorer.success') : IO::getLastError(LNG('explorer.error'));
		show_json($msg,!!$res);
	}
	// 回滚到最新; 将某个版本设置为当前版本;
	public function rollback() {
		$res = IOHistory::rollback($this->path,$this->in['id']);
		$msg = $res ? LNG('explorer.success') : IO::getLastError(LNG('explorer.error'));
		show_json($msg,!!$res);
	}
	public function setDetail(){
		$maxLength = $GLOBALS['config']['systemOption']['historyDescLengthMax'];
		$msg  = LNG('common.lengthLimit').'('.LNG('explorer.noMoreThan').$maxLength.')';
		$data = Input::getArray(array(
			'detail'=> array('check'=>'length','param'=>array(0,$maxLength),'msg'=>$msg),
		));
		$res = IOHistory::setDetail($this->path,$this->in['id'],$data['detail']);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.dataError');
		show_json($msg,!!$res);
	}
	public function fileOut(){
		IOHistory::fileOut($this->path,$this->in['id']);
	}
	public function fileInfo(){
		return IOHistory::fileInfo($this->path,$this->in['id']);
	}
}
index.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/index.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerIndex extends Controller{
	private $model;
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	public function pathInfo(){
		$fileList = json_decode($this->in['dataArr'],true);
		if(!$fileList){
			show_json(LNG('explorer.error'),false);
		}
		$result = array();
		for ($i=0; $i < count($fileList); $i++){// 处理掉无权限和不存在的内容;
			$item = $fileList[$i];
			if(isset($item['pathTrue']) && $item['pathTrue'] == '1'){
				$item['path'] = kodIO::pathTrue($item['path']); // 相对路径获取处理;
			}
			if(!Action('explorer.auth')->can($item['path'],'show')) continue;
			
			$itemInfo = $this->itemInfo($item);
			if($itemInfo){$result[] = $itemInfo;}
		}
		if(count($fileList) == 1 && $result){
			$result = $this->itemInfoMore($result[0]);
		}
		$data = !!$result ? $result : LNG('common.pathNotExists');
		show_json($data,!!$result);
	}
	private function itemInfo($item){
		$path = $item['path'];
		$type = _get($item,'type');
		try{// 收藏或访问的io路径不存在情况报错优化;
			if($this->in['getChildren'] == '1'){
				$result = IO::infoWithChildren($path);
			}else if($type == 'simple'){
				$result = IO::info($path);
			}else{
				$result = IO::infoFull($path);
			}
		}catch(Exception $e){
			$result = false;
		}
		
		if(!$result) return false;
		// $canLink = Action('explorer.auth')->fileCanDownload($path);
		$canLink = Action('explorer.auth')->fileCan($path,'edit');//edit,share; 有编辑权限才能生成外链;
		if( $result['type'] == 'file' && $canLink){
			$result['downloadPath'] = Action('explorer.share')->link($path);
		}
		$result = Action('explorer.list')->pathInfoParse($result,0,1);
		if($result['isDelete'] == '1'){unset($result['downloadPath']);}
		return $result;
	}

	public  function pathInfoItem($path){
		$pathInfo = IO::info($path);
		if(!$pathInfo) return false;
		return $this->itemInfoMore($pathInfo);
	}
	private function itemInfoMore($item){
		$result  = Model('SourceAuth')->authOwnerApply($item);
		$showMd5 = Model('SystemOption')->get('showFileMd5') != '0';
		if($result['type'] != 'file') return $item;
		
		if($showMd5 && !_get($result,'fileInfo.hashMd5') && 
			($result['size'] <= 100*1024*1024 || _get($this->in,'getMore') || _get($this->in,'getChildren'))  ){
			$result['hashMd5'] = IO::hashMd5($result['path']);
		}
		$result = Action('explorer.list')->pathInfoMore($result);
		$result = Action('explorer.list')->fileInfoAddHistory($result);
		return $result;
	}
	
	public function desktopApp(){
		$desktopApps = include(BASIC_PATH.'data/system/desktop_app.php');
		$desktopApps['myComputer']['value'] = MY_HOME;// {source:home} 不指定打开文件夹,打开最后所在文件夹;

		if( !Action('explorer.listBlock')->pathEnable('my') ){
			unset($desktopApps['myComputer']);
		}
		if($this->config['settings']['disableDesktopHelp'] == 1){
			unset($desktopApps['userHelp']);
		}
		foreach ($desktopApps as $key => &$item) {
			if($item['menuType'] == 'menu-default-open'){
				$item['menuType'] = 'menu-default';
			}
			if(!KodUser::isRoot() && $item['rootNeed']){
				unset($desktopApps[$key]);
			}
		};unset($item);
		show_json($desktopApps);
	}

	/**
	 * 设置文档描述;
	 */
	public function setDesc(){
		$maxLength = $GLOBALS['config']['systemOption']['fileDescLengthMax'];
		$msg = LNG('explorer.descTooLong').'('.LNG('explorer.noMoreThan').$maxLength.')';
		$data = Input::getArray(array(
			'path'	=> array('check'=>'require'),
			'desc'	=> array('check'=>'length','param'=>array(0,$maxLength),'msg'=>$msg),
		));
		
		$result = false;
		$info   = IO::infoSimple($data['path']);
		if($info && $info['sourceID']){
			$result = $this->model->setDesc($info['sourceID'],$data['desc']);
		}
		// $msg = !!$result ? LNG('explorer.success') : LNG('explorer.error');
		show_json($data['desc'],!!$result);
	}
	
	/**
	 * 设置文档描述;
	 */
	public function setMeta(){
		$this->thumbClear();
		$data = Input::getArray(array(
			'path'	=> array('check'=>'require'),
			'data'	=> array('check'=>'require'),
		));
		$meta = json_decode($data['data'],true);
		if(!$meta || !is_array($meta)){
			show_json(LNG('explorer.error'),false);
		}

		$info = IO::info($data['path']);
		$this->sourceSecretApply($meta,$info);
		if($info && $info['sourceID']){
			$metaArr = array();
			foreach ($meta as $key => $value) {
				if( !$this->metaKeyCheck($key,$value,$info) ){
					show_json("key error!",false);
				}
				$metaArr[$key] = $value === '' ? null:$value; //为空则删除;
				$this->model->metaSet($info['sourceID'],$key,$value);
			}
			
			// 设置文件夹密码,自动设置密码用户为当前用户;
			if(isset($metaArr['folderPassword'])){
				$metaArr['folderPasswordUser'] = $metaArr['folderPassword'] ? USER_ID:null;
			}
			$this->model->metaSet($info['sourceID'],$metaArr);
			show_json(IO::info($data['path']),true);
		}
		show_json(LNG('explorer.error'),false);
	}
	private function metaKeyCheck($key,$value,$info){
		static $metaKeys = false;
		if(!$metaKeys){
			$metaKeys = array_keys($this->config['settings']['sourceMeta']);
			$metaKeys = array_merge($metaKeys,array(
				'systemSort',		// 置顶
				'systemLock',		// 编辑锁定
				'systemLockTime',	// 编辑锁定时间
				
				'folderPassword',		// 文件夹密码
				'folderPasswordDesc',	// 文件夹密码描述
				'folderPasswordTimeTo',	// 文件夹密码过期时间,为空则代表不限制时间;
			));
		}
		$isLock = _get($info,'metaInfo.systemLock') ? true:false;
		if($key == "systemLock" && $value && $isLock){
			show_json(LNG('explorer.fileLockError'),false);
		}
		return in_array($key,$metaKeys);
	}
	
	// 文档密级处理;
	private function sourceSecretApply(&$meta,$pathInfo){
		$key = 'user_sourceSecret';
		if(!isset($meta[$key])) return;
		$sourceSecret = $meta[$key].'';
		unset($meta[$key]);
		// Model('SourceSecret')->clear(); //清除所有 debug;
		// Model("SystemOption")->set(array('sourceSecretList'=>'','sourceSecretMaxID'=>''));
		
		// 检测支持: 是否开启密级;自己是否为系统管理员或密级管理者; 是否为部门文档;
		$systemOption = Model("SystemOption")->get();
		if($pathInfo['targetType'] != 'group') return;
		if($systemOption['sourceSecret'] != '1') return;
		$allowUser  = explode(',',$systemOption['sourceSecretSetUser']);
		if(!KodUser::isRoot() && !in_array(USER_ID,$allowUser)) return;
		
		$sourceID = $pathInfo['sourceID'];
		$model = Model('SourceSecret');
		$find  = $model->findByKey('sourceID',$sourceID);
		$data  = array('sourceID'=>$sourceID,'typeID'=>$sourceSecret,'createUser'=>USER_ID);
		if($sourceSecret){
			if($find){$model->update($find['id'],$data);}
			if(!$find){$model->insert($data);}
		}else{
			if($find){$model->remove($find['id']);}
		}
	}
	

	// 清除文件缩略图
	private function thumbClear(){
		$data = Input::getArray(array(
			'path'	=> array('check'=>'require'),
			'clear'	=> array('default'=>0),
		));
		if ($data['clear'] != '1') return;
		if (!IO::exist(IO_PATH_SYSTEM_TEMP)) show_json(LNG('explorer.success'));
		
		// 文件封面插件缩略图清理
		$fileInfo = IO::info($data['path']);
		$fileThumbInfo = IO::infoFullSimple(IO_PATH_SYSTEM_TEMP . 'plugin/fileThumb');
		if($fileThumbInfo && isset($fileThumbInfo['path'])) {
			$hasFind   = false;
			$fileHash  = KodIO::hashPath($fileInfo);
			$thumbList = array(250,600,1200,2000,3000,5000);	// 缩略图尺寸
			// 同时删除缩略图目录下fileID对应的所有记录(即包括其他文件生成的相同缩略图)
			$coverList = array();
			foreach ($thumbList as $width){
				$coverName = "cover_{$fileHash}_{$width}.png";
				$coverList[] = $coverName;
				// $coverName = "cover_{$fileHash}_{$width}.jpg";	// 缩略图格式改为jpg,暂不处理
				// $coverList[] = $coverName;
			}
			$parentID = KodIO::sourceID($fileThumbInfo['path']);
			$where = array(
				's.parentID'	=> $parentID,
				's.name'		=> array('in',$coverList),
				's.isDelete'	=> 0
			);
			$list = Model('Source')->alias('s')->field('s2.sourceID,s2.name')
					->join("INNER JOIN `io_source` s2 ON s.parentID = s2.parentID AND s.fileID = s2.fileID")
					->where($where)->select();
			if (!$list) $list = array();
			foreach ($list as $item){
				$sourceID = $item['sourceID'];
				Cache::remove($item['name']);
				if(!$sourceID){continue;}
				$hasFind = true;
				IO::remove(KodIO::make($sourceID),false);
			}
			if($hasFind){IO::setModifyTime($data['path'],time());} // 有缩略图清除,则更新最后修改时间,防止浏览器缓存
		}
		
		// 删除元数据
		$cacheKey = 'fileInfo.'.md5($fileInfo['path'].'@'.$fileInfo['size'].$fileInfo['modifyTime']);
		$fileID   = _get($fileInfo,'fileID');
		$fileID   = _get($fileInfo,'fileInfo.fileID',$fileID);
		Cache::remove($cacheKey);
		if($fileInfo['sourceID']){Model('Source')->metaSet($fileInfo['sourceID'],'modifyTimeShow',time());}
		if(_get($fileInfo,'metaInfo.user_sourceCover')){ // 清空缩略图,包含清空文件设定的封面;
			Model('Source')->metaSet($fileInfo['sourceID'],'user_sourceCover');
		}
		if($fileID){Model("File")->metaSet($fileID,'fileInfoMore',null);};
		show_json(LNG('explorer.success'));
	}

	/**
	 * 设置权限
	 */
	public function setAuth(){
		$result = false;
		$actionAllow = array(
			'getData','clearChildren','getAllParent','getAllChildren','getGroupUser',
			'getAllChildrenByUser','setAllChildrenByUser','chmod',
		);
		$data = Input::getArray(array(
			'path'	=> array('check'=>'require'),
			'auth'	=> array('check'=>'json','default'=>array()),
			'action'=> array('check'=>'in','default'=>'','param'=>$actionAllow),
		));
		
		// local,chmod;
		if($data['action'] == 'chmod'){
			$mode = intval($this->in['auth'],8);
			if($mode){$result = chmod_path($data['path'],$mode);}
			$msg = !!$result ? LNG('explorer.success') : LNG('explorer.error');
			show_json($msg,!!$result);
		}
		
		$info   = IO::info($data['path']);
		if( $info && $info['sourceID'] && $info['targetType'] == 'group'){//只能设置部门文档;
			$groupID = $info['targetID'];
			if($data['action'] == 'getData'){
				$result = Model('SourceAuth')->getAuth($info['sourceID']);
				show_json($result);
			}else if($data['action'] == 'clearChildren'){
				//清空所有子文件(夹)的权限;
				$result = Model('SourceAuth')->authClear($info['sourceID']);
			}else if($data['action'] == 'getAllChildren'){
				//该文件夹下所有单独设置过权限的内容; 按层级深度排序-由浅到深(文件夹在前)
				$result = Model('SourceAuth')->getAllChildren($info['sourceID']);
				$result = array_page_split($result);
				show_json($result,true);
			}else if($data['action'] == 'getAllParent'){
				//该文件夹所有有权限的用户或部门;向上回溯全部查询; 有权限的用户列表(用户/部门,最终合并计算后的权限;权限来源)
				$result = Model('SourceAuth')->getAllParent($info['sourceID']);
				$result = array_page_split($result);
				show_json($result,true);
			}else if($data['action'] == 'getAllChildrenByUser'){
				//该文件夹下所有针对某用户设置或权限的内容;
				$result = Model('SourceAuth')->getAllChildrenByUser($info['sourceID'],$this->in['userID']);
				$result = array_page_split($result);
				show_json($result,true);
			}else if($data['action'] == 'setAllChildrenByUser'){
				//重置该文件夹下所有针对某用户设置权限的权限;
				$result = Model('SourceAuth')->setAllChildrenByUser($info['sourceID'],$this->in['userID'],$this->in['authID']);
				show_json($result ? LNG('explorer.success'): LNG('explorer.error'),true);
			}else if($data['action'] == 'getGroupUser'){
				//部门成员在该部门的初始权限; 按权限大小排序
				$result = Model('User')->listByGroup($groupID);
				foreach($result['list'] as $index=>$userInfo){
					// $userInfo = Model('User')->getInfoSimpleOuter($userInfo['userID']);
					$groupAuth = array_find_by_field($userInfo['groupInfo'],'groupID',$groupID);
					$userInfo['groupAuth']  = $groupAuth ? $groupAuth['auth'] : false;
					$result['list'][$index] = $userInfo;
				}
				// 按权限高低排序;
				$result['list'] = array_sort_by($result['list'],'groupAuth.auth',true);
				show_json($result,true);
			}else{
				$setAuth = $this->setAuthSelf($info,$data['auth']);
				$result = Model('SourceAuth')->setAuth($info['sourceID'],$setAuth);
			}
		}
		$msg = !!$result ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$result);
	}
	
	// 设置权限.默认设置自己为之前管理权限; 如果只有自己则清空;
	private function setAuthSelf($pathInfo,$auth){
		if(!$auth) return $auth;
		$selfAuth = _get($pathInfo,'auth.authInfo.id');
		$authList = array();
		foreach($auth as $item){
			if( $item['targetID'] == USER_ID && 
				$item['targetType'] == SourceModel::TYPE_USER){
				continue;
			}
			$authList[] = $item;
		}
		if(!$authList || !$selfAuth) return $authList;
		$authList[] = array(
			'targetID' 	=> USER_ID, 
			'targetType'=> SourceModel::TYPE_USER,
			'authID' 	=> $selfAuth
		);
		return $authList;
	}
	
	public function pathAllowCheck(&$path){
		$notAllow = array('/', '\\', ':', '*', '?', '"', '<', '>', '|');
		$db = $this->config['database'];// 文件文件夹名emoji是否支持处理;
		if(!isset($db['DB_CHARSET']) || $db['DB_CHARSET'] != 'utf8mb4'){
			$path = preg_replace_callback('/./u',function($match){return strlen($match[0]) >= 4 ? '-':$match[0];},$path);
		}
		
		$name  = trim(get_path_this($path));
		$parse = KodIO::parse($path);
		if($parse['pathBase']){$name = trim(get_path_this($parse['param']));}
		if(!$name){return;} // 允许纯为空情况; 新建文件夹: {source:xxx}/ 允许该情况;
		
		$checkName = str_replace($notAllow,'_',$name);
		if($name != $checkName){
		    show_json(LNG('explorer.charNoSupport').implode(',',$notAllow),false);
		}
		
		$maxLength = $GLOBALS['config']['systemOption']['fileNameLengthMax'];
		if($maxLength && strlen($name) > $maxLength ){
			show_json(LNG("common.lengthLimit")." (max=$maxLength)",false);
		}
		return;
	}
	
	public function mkfile(){
		$this->pathAllowCheck($this->in['path']);
		$info = IO::info($this->in['path']);
		if($info && $info['type'] == 'file'){ //父目录为文件;
			show_json(LNG('explorer.success'),true,IO::pathFather($info['path']));
		}
		
		$tplPath = BASIC_PATH.'static/others/newfile-tpl/';
		$ext     = get_path_ext($this->in['path']);
		$tplFile = $tplPath.'newfile.'.$ext;
		$content = _get($this->in,'content','');
		if($content){
			if(_get($this->in,'base64') ){$content = base64_decode($content);}
		}else if(@file_exists($tplFile)){
			$content = file_get_contents($tplFile);
		}
		$repeat = !empty($this->in['fileRepeat']) ? $this->in['fileRepeat']:REPEAT_RENAME;
		$result = IO::mkfile($this->in['path'],$content,$repeat);
		
		$errorLast = IO::getLastError(LNG('explorer.error'));
		$msg = !!$result ? LNG('explorer.success') : $errorLast;
		show_json($msg,!!$result,$result);
	}
	public function mkdir(){
		$this->pathAllowCheck($this->in['path']);
		$repeat = !empty($this->in['fileRepeat']) ? $this->in['fileRepeat']:REPEAT_SKIP;
		$info = IO::info($this->in['path']);
		if($info && $info['type'] == 'file'){ //父目录为文件;
			show_json(LNG('explorer.success'),true,IO::pathFather($info['path']));
		}
		
		$result = IO::mkdir($this->in['path'],$repeat);
		$errorLast = IO::getLastError(LNG('explorer.error'));
		$msg = !!$result ? LNG('explorer.success') : $errorLast;
		show_json($msg,!!$result,$result);
	}
	public function pathRename(){
		$this->pathAllowCheck($this->in['newName']);
		$path = $this->in['path'];
		$this->taskCopyCheck(array(array("path"=>$path)));
		
		$result = IO::rename($path,$this->in['newName']);
		$errorLast = IO::getLastError(LNG('explorer.pathExists'));
		$msg = !!$result ? LNG('explorer.success') : $errorLast;
		show_json($msg,!!$result,$result);
	}

	public function pathDelete(){
		$list = json_decode($this->in['dataArr'],true);
		$this->taskCopyCheck($list);
		$toRecycle = Model('UserOption')->get('recycleOpen');
		if( _get($this->in,'shiftDelete') == '1' ){
			$toRecycle = false;
		}
		$success=0;$error=0;
		foreach ($list as $val) {
			$result = Action('explorer.recycleDriver')->removeCheck($val['path'],$toRecycle);
			$result ? $success ++ : $error ++;
		}
		$code = $error === 0 ? true:false;
		$errorLast = IO::getLastError(LNG('explorer.removeFail'));
		$msg  = $code ? LNG('explorer.removeSuccess') : $errorLast;
		if(!$code && $success > 0){
			$msg = $success.' '.LNG('explorer.success').', '.$error.' '.$errorLast;
		}
		show_json($msg,$code);
	}
	// 从回收站删除
	public function recycleDelete(){		
		$pathArr   = false;
		if( _get($this->in,'all') ){
			$recycleList = Model('SourceRecycle')->listData();
			foreach ($recycleList as $key => $sourceID) {
				$recycleList[$key] = array("path"=>KodIO::make($sourceID));
			}
			$this->taskCopyCheck($recycleList);//彻底删除: children数量获取为0,只能是主任务计数;
		}else{
			$dataArr = json_decode($this->in['dataArr'],true);
			$this->taskCopyCheck($dataArr);
			$pathArr = $this->parseSource($dataArr);
		}
		Model('SourceRecycle')->remove($pathArr);
		Action('explorer.recycleDriver')->remove($pathArr);

		// 清空回收站时,重新计算大小; 一小时内不再处理;
		Model('Source')->targetSpaceUpdate(SourceModel::TYPE_USER,USER_ID);
		$cacheKey = 'autoReset_'.USER_ID;
		if(isset($this->in['all']) && time() - intval(Cache::get($cacheKey)) > 3600 * 10 ){
			Cache::set($cacheKey,time());
			$USER_HOME = KodIO::sourceID(MY_HOME);
			Model('Source')->folderSizeResetChildren($USER_HOME);
			Model('Source')->userSpaceReset(USER_ID);
		}
		show_json(LNG('explorer.success'));
	}
	//回收站还原
	public function recycleRestore(){
		$pathArr = false;
		if( _get($this->in,'all') ){
			$recycleList = Model('SourceRecycle')->listData();
			foreach ($recycleList as $key => $sourceID) {
				$recycleList[$key] = array("path"=>KodIO::make($sourceID));
			}
			$this->taskCopyCheck($recycleList);
		}else{
			$dataArr = json_decode($this->in['dataArr'],true);
			$this->taskCopyCheck($dataArr);
			$pathArr = $this->parseSource($dataArr);
		}

		Action('explorer.recycleDriver')->restore($pathArr);
		Model('SourceRecycle')->restore($pathArr);
		show_json(LNG('explorer.success')); 
	}
	private function parseSource($list){
		$result = array();
		foreach ($list as $value) {
			$parse = KodIO::parse($value['path']);
			$thePath = $value['path'];// io路径;物理路径;协作分享路径处理保持不变;
			if($parse['type'] == KodIO::KOD_SOURCE){
				$thePath = IO::getPath($value['path']);
			}
			$result[] = $thePath;
		}
		return $result;
	}

	
	public function pathCopy(){
		Session::set(array(
			'pathCopyType'	=> 'copy',
			'pathCopy'		=> $this->in['dataArr'],
		));
		show_json(LNG('explorer.copySuccess'));
	}
	public function pathCute(){
		Session::set(array(
			'pathCopyType'	=> 'cute',
			'pathCopy'		=> $this->in['dataArr'],
		));
		show_json(LNG('explorer.cuteSuccess'));
	}
	public function pathCopyTo(){
		$this->pathPast('copy',$this->in['dataArr']);	
	}
	public function pathCuteTo(){
		$this->pathPast('cute',$this->in['dataArr']);	
	}
	public function clipboard(){
		if(isset($this->in['clear'])){
			Session::set('pathCopy', json_encode(array()));
			Session::set('pathCopyType','');
			return;
		}
		$clipboard = json_decode(Session::get('pathCopy'),true);
		if(!$clipboard){
			$clipboard = array();
		}
		show_json($clipboard,true,Session::get('pathCopyType'));
	}
	public function pathLog(){
		$info = IO::info($this->in['path']);
		if(!$info['sourceID']){
			show_json('path error',false);
		}
		$data = Model('SourceEvent')->listBySource($info['sourceID']);
		
		// 协作分享;路径数据处理;
		if($info['shareID']){
			$shareInfo	= Model('Share')->getInfo($info['shareID']);
			$userActon  = Action('explorer.userShare');
			foreach($data['list'] as $i=>$item){
				if($item['sourceInfo']){
					$data['list'][$i]['sourceInfo'] = $userActon->_shareItemeParse($item['sourceInfo'],$shareInfo);
				}
				if($item['parentInfo']){
					$data['list'][$i]['parentInfo'] = $userActon->_shareItemeParse($item['parentInfo'],$shareInfo);
				}
				if(!is_array($item['desc'])){continue;}
				if(is_array($item['desc']['from'])){
					$data['list'][$i]['desc']['from'] = $userActon->_shareItemeParse($item['desc']['from'],$shareInfo);
				}
				if(is_array($item['desc']['to'])){
					$data['list'][$i]['desc']['to'] = $userActon->_shareItemeParse($item['desc']['to'],$shareInfo);
				}
				if(is_array($item['desc']['sourceID'])){
					$data['list'][$i]['desc']['sourceID'] = $userActon->_shareItemeParse($item['desc']['sourceID'],$shareInfo);
				}
			}
		}
		show_json($data);
	}

	/**
	 * 复制或移动
	 */
	public function pathPast($copyType=false,$list=false){
		if(!$copyType){
			$copyType = Session::get('pathCopyType');
			$list     = Session::get('pathCopy');
			if($copyType == 'cute'){
				Session::set('pathCopy', json_encode(array()));
				Session::set('pathCopyType', '');
			}
		}

		$list = json_decode($list,true);
		$list = is_array($list) ? $list : array();
		if($copyType == 'copy'){
			$list = $this->copyCheckShare($list);
		}
		$pathTo = $this->in['path'];
		if (count($list) == 0 || !$pathTo) {
			show_json(LNG('explorer.clipboardNull'),false);
		}
		ignore_timeout(0);
		$this->taskCopyCheck($list);
		
		Hook::trigger('explorer.pathCopyMove',$copyType,$list);
		$repeat = Model('UserOption')->get('fileRepeat');
		$repeat = !empty($this->in['fileRepeat']) ? $this->in['fileRepeat'] :$repeat;
		$result = array();$errorList = array();
		
		// 所有操作中,是否有重名覆盖的情况(文件,文件夹都算)
		$infoMore = array('hasExistAll'=>false,'pathTo'=>$pathTo,'listFrom'=>$list,'listTo'=>array());
		for ($i=0; $i < count($list); $i++) {
			$thePath = $list[$i]['path'];
			$repeatType = $repeat;
			$ioInfo 	= IO::info($thePath);
			$driverTo 	= IO::init($this->in['path']);
			$hasExists  = $driverTo->fileNameExist($driverTo->path,$ioInfo['name']);

			if($copyType == 'copy') {
				//复制到自己所在目录,则为克隆;
				$driver = IO::init($thePath);
				$father = $driver->getPathOuter($driver->pathFather($driver->path));
				if(KodIO::clear($father) == KodIO::clear($pathTo) ){
					$repeatType = REPEAT_RENAME_FOLDER;
				}
				$itemResult = IO::copy($thePath,$pathTo,$repeatType);
			}else{
				$itemResult = IO::move($thePath,$pathTo,$repeatType);
			}
			
			// 复制/移动时; 所有内容是否存在文件夹已存在覆盖,文件已存在覆盖的情况; 存在时前端不支持撤销操作;
			if($hasExists){
				if($ioInfo['type'] == 'file' && ($repeatType != REPEAT_RENAME && $repeatType != REPEAT_RENAME_FOLDER)){
					$infoMore['hasExistAll'] = true;
				}
				if($ioInfo['type'] == 'folder' && $repeatType != REPEAT_RENAME_FOLDER){
					$infoMore['hasExistAll'] = true;
				}
			}
			if(!$itemResult){$errorList[] = $thePath;continue;}
			$result[] = $itemResult;
			$infoMore['listTo'][] = array('path'=>$itemResult);
		}
		$code = $result ? true:false;
		$msg  = $copyType == 'copy'?LNG('explorer.pastSuccess'):LNG('explorer.cutePastSuccess');
		
		if(count($result) == 0){$msg = IO::getLastError(LNG('explorer.error'));}
		if($errorList){$msg .= "(".count($errorList)." error)";}
		show_json($msg,$code,$result,$infoMore);
	}
	
	// 外链分享复制;
	private function copyCheckShare($list){
		for ($i=0; $i < count($list); $i++) {
			$path = $list[$i]['path'];
			$pathParse= KodIO::parse($path);
			if($pathParse['type'] != KodIO::KOD_SHARE_LINK) continue;
			
			// 外链分享处理; 权限限制相关校验; 关闭下载--不支持转存; 转存数量限制处理;
			$info = Action('explorer.share')->sharePathInfo($path);
			$shareInfo = Action('explorer.share')->shareInfoLast();
			if(!$info || !$shareInfo){
				show_json($GLOBALS['explorer.sharePathInfo.error'], false);
			}
			if($shareInfo['options'] && $shareInfo['options']['notDownload'] == '1'){
				show_json(LNG('explorer.share.noDownTips'), false);
			}
			$list[$i]['path'] = $info['path'];
		}
		return $list;
	}

	// 文件移动; 耗时任务;
	private function taskCopyCheck($list){
		$list = is_array($list) ? $list : array();
		$defaultID = 'copyMove-'.USER_ID.'-'.rand_string(8);
		$taskID = $this->in['longTaskID'] ? $this->in['longTaskID']:$defaultID;
		
		$task = new TaskFileTransfer($taskID,'copyMove');
		$task->update(0,true);//立即保存, 兼容文件夹子内容过多,扫描太久的问题;
		for ($i=0; $i < count($list); $i++) {
			$task->addPath($list[$i]['path']);
		}
	}
	
	/**
	 * 压缩下载
	 */
	public function fileDownloadRemove(){
		$path = Input::get('path', 'require');
		$path = $this->pathCrypt($path,false);
		if(!$path || !IO::exist($path)) {
			show_json(LNG('common.pathNotExists'), false);
		}
		IO::fileOut($path,true);
		$dir = get_path_father($path);
		if(strstr($dir,TEMP_FILES)){
		    del_dir($dir);
		}
	}
	
	public function clearCache(){
		$maxTime = 3600*24;
		$list = IO::listPath(TEMP_FILES);
		$list = is_array($list) ? $list : array('fileList'=>array(),'folderList'=>array());
		$list = array_merge($list['fileList'],$list['folderList']);
		foreach($list as $item){
			if(time() - $item['modifyTime'] < $maxTime) continue;
			if(is_dir($item['path'])){
				del_dir($item['path']);
			}else{
				del_file($item['path']);
			}
		}
	}
	/**
	 * 多文件、文件夹压缩下载
	 * @return void
	 */
	public function zipDownload(){	
		$dataArr  = json_decode($this->in['dataArr'],true);
		// 前端压缩处理;
		if($this->in['zipClient'] == '1'){
			return show_json($this->zipDownloadClient($dataArr),true);
		}
		
		ignore_timeout();
		$zipFolder = md5(json_encode(sort(array_to_keyvalue($dataArr,'','path'))));
		$zipCache  = TEMP_FILES.$zipFolder.'/';
		mk_dir($zipCache);file_put_contents($zipCache.'index.html','');
		$zipPath   = Cache::get($zipFolder);
		if($zipPath && IO::exist($zipPath) ){
			return $this->zipDownloadStart($zipPath);
		}
		$zipPath = $this->zip($zipCache);
		Cache::set($zipFolder, $zipPath, 3600*6);
		$this->zipDownloadStart($zipPath);
	}
	private function zipDownloadStart($zipPath){
		if(isset($this->in['disableCache']) && $this->in['disableCache'] == '1'){
			if(!$zipPath || !IO::exist($zipPath)) return;
			IO::fileOut($zipPath,true);
			return;
		}
		show_json(LNG('explorer.zipSuccess'),true,$this->pathCrypt($zipPath));
	}
	
	// 文件名加解密
	public function pathCrypt($path, $en=true){
		$pass = Model('SystemOption')->get('systemPassword').'encode';
		return $en ? Mcrypt::encode($path,$pass) : Mcrypt::decode($path,$pass);
	}
	
	public function zipDownloadClient($dataArr){
		$result  = array();
		foreach($dataArr as $itemZip){
			$pathInfo   = IO::info($itemZip['path']);
			$isFolder   = $itemZip['type'] == 'folder';
			$itemZipOut = array('path'=>'/'.$itemZip['name'],'folder'=>$isFolder);
			$itemZipOut['modifyTime'] = $pathInfo['modifyTime'];

			if(!$isFolder){
				$itemZipOut['filePath'] = $itemZip['path'];
				$itemZipOut['size'] = $pathInfo['size'];
				$result[] = $itemZipOut;continue;
			}
			$result[] = $itemZipOut;
			$children = IO::listAllSimple($itemZip['path'],1);
			$result   = array_merge($result, $children);
		}
		return $result;
	}

	/**
	 * 压缩
	 * @param string $zipPath
	 */
	public function zip($zipPath=''){
		ignore_timeout();
		$dataArr  = json_decode($this->in['dataArr'],true);
		$zipLimit = Model('SystemOption')->get('downloadZipLimit');
		$task 	  = $this->taskZip($dataArr);
		if($zipLimit && $zipLimit > 0){
			$zipLimit  = floatval($zipLimit) * 1024 * 1024 * 1024;
			$totalSize = intval($task->task['sizeTotal']);
			if($totalSize > $zipLimit){
				$limitTips = '('.size_format($zipLimit).')';
				show_json(LNG('admin.setting.downloadZipLimitTips').$limitTips,false);
			}
		}
		
		$fileType = Input::get('type', 'require','zip');
		$repeat   = Model('UserOption')->get('fileRepeat');
		$repeat   = !empty($this->in['fileRepeat']) ? $this->in['fileRepeat'] :$repeat;

		$zipFile = IOArchive::zip($dataArr, $fileType, $zipPath,$repeat);
		if($zipPath != '') return $zipFile;
		$info = IO::info($zipFile);
		$data = LNG('explorer.zipSuccess').LNG('explorer.file.size').":".size_format($info['size']);
		show_json($data,true,$zipFile);
	}
	
	private function taskZip($list){
		$list = is_array($list) ? $list : array();
		$defaultID = 'zip-'.USER_ID.'-'.rand_string(8);
		$taskID = $this->in['longTaskID'] ? $this->in['longTaskID']:$defaultID;
		$task = new TaskZip($taskID,'zip');
		$task->update(0,true);//立即保存, 兼容文件夹子内容过多,扫描太久的问题;
		for ($i=0; $i < count($list); $i++) {
			$task->addPath($list[$i]['path']);
		}
		return $task;
	}
	private function taskUnzip($path){
		$defaultID = 'unzip-'.USER_ID.'-'.rand_string(8);
		$taskID = $this->in['longTaskID'] ? $this->in['longTaskID']:$defaultID;
		$task = new TaskUnzip($taskID,'zip');
		$task->update(0,true);//立即保存,部分解压方式不触发任务更新,导致进度获取失败
		$task->addFile($path);
	}
	
	/**
	 * 解压缩
	 */
	public function unzip(){
		ignore_timeout();
		$data = Input::getArray(array(
			'path' => array('check' => 'require'),
			'pathTo' => array('check' => 'require'),
			'unzipPart' => array('check' => 'require', 'default' => '-1')
		));
		
		$repeat = Model('UserOption')->get('fileRepeat');
		$repeat = !empty($this->in['fileRepeat']) ? $this->in['fileRepeat'] :$repeat;
		$this->taskUnzip($data['path']);
		$result = IOArchive::unzip($data['path'],$data['pathTo'],$data['unzipPart'],$repeat);
		show_json($result ? LNG('explorer.unzipSuccess'):LNG('explorer.error'),!!$result,$result);
	}

	/**
	 * 查看压缩文件列表
	 */
	public function unzipList(){
		$data = Input::getArray(array(
			'path' => array('check' => 'require'),
			'index' => array('check' => 'require', 'default' => '-1'),
			'download' => array('check' => 'require', 'default' => false),
		));
		$this->taskUnzip($data['path']);
		$list = IOArchive::unzipList($data);
		$this->updateLastOpen($data['path']);
		show_json($list);
	}

	public function fileDownload(){
		$this->in['download'] = 1;
		$this->fileOut();
	}
	//输出文件
	public function fileOut(){
		$path = $this->in['path'];
		if(!$path) return; 
		$isDownload   = isset($this->in['download']) && $this->in['download'] == 1;
		$downFilename = !empty($this->in['downFilename']) ? $this->in['downFilename'] : false;
		if($isDownload && !Action('user.authRole')->authCanDownload()){
			show_json(LNG('explorer.noPermissionAction'),false);
		}
		if($isDownload){Hook::trigger('explorer.fileDownload', $path);}
		Hook::trigger('explorer.fileOut',$path);
		if(isset($this->in['type']) && $this->in['type'] == 'image'){
			$info = IO::info($path);
			$imageThumb = array('jpg','png','jpeg','bmp');
			$width = isset($this->in['width']) ? intval($this->in['width']) :0;
			if($isDownload || !$width || $width >= 2000){
				$this->updateLastOpen($path);
			}
			if($info['size'] >= 1024*200 &&
				function_exists('imagecolorallocate') &&
				in_array($info['ext'],$imageThumb) 
			){return IO::fileOutImage($path,$width);}
			return IO::fileOut($path,$isDownload,$downFilename); // 不再记录打开时间;
		}
		$this->fileOutUpdate($path,$isDownload,$downFilename);
	}
	/*
	相对某个文件访问其他文件; 权限自动处理;支持source,分享路径,io路径,物理路径;
	path={source:1138926}/&add=images/as.png; path={source:1138926}/&add=as.png
	path={shareItem:123}/1138934/&add=images/as.png
	
	相对路径支持;local,io,(source),shareLink,shareItem[local,io,(source)]
	/a/b/.././c/../1.txt => /a/1.txt;
	*/
	public function fileOutBy(){
		if(!$this->in['path']) return; 
		
		// 拼接转换相对路径;
		$add   = kodIO::pathUrlClear(rawurldecode($this->in['add']));
		$parse = kodIO::parse($this->in['path']);
		$allow = array('',false,kodIO::KOD_IO,kodIO::KOD_USER_DRIVER,kodIO::KOD_SHARE_LINK);
		if(in_array($parse['type'],$allow)){
			$distPath = kodIO::pathTrue($parse['path'].'/../'.$add);
			$distInfo = IO::info($distPath);
		}else{//KOD_SOURCE KOD_SHARE_ITEM(source,)
			$info = IO::info($parse['path']);
			if($parse['type'] == kodIO::KOD_SOURCE){
				$level = Model("Source")->parentLevelArray($info['parentLevel']);
				$pathRoot = '{source:'.$level[0].'}';
			}else if($parse['type'] == kodIO::KOD_SHARE_ITEM){
				$pathArr   = explode('/',trim($parse['path'],'/'));
				$pathRoot  = $pathArr[0];
				$shareInfo = Model('Share')->getInfo($parse['id']); // source路径内部协作分享;
				if($shareInfo['sourceID']){$pathRoot = $pathRoot.'/'.$shareInfo['sourceID'];}
			}
			
			$displayPathArr = explode('/',trim($info['pathDisplay'],'/'));array_shift($displayPathArr);
			$displayPath = $pathRoot.'/'.implode('/',$displayPathArr);
			$distPath = kodIO::pathTrue($displayPath.'/../'.$add);
			$distInfo = IO::infoFullSimple($distPath);
		}
		// pr($distPath,$distInfo,$parse,[$pathRoot,$displayPath,$info,$shareInfo]);exit;
		if(!$distInfo || $distInfo['type'] != 'file'){
			return show_json(LNG('common.pathNotExists'),false);
		}
		if(isset($this->in['type']) && $this->in['type'] == 'getTruePath'){
			show_json($distInfo['path'],true);
		}
		
		ActionCall('explorer.auth.canView',$distInfo['path']);// 再次判断新路径权限;
		Hook::trigger('explorer.fileOut', $distInfo['path']);
		$this->fileOutUpdate($distInfo['path'],false);
	}
	
	public function fileOutUpdate($path,$isDownload=false,$downFilename=''){
		$this->updateLastOpen($path);
		IO::fileOut($path,$isDownload,$downFilename);
	}
	
	// 打开文档,更新最后打开时间
	public function updateLastOpen($path){
		static  $LAST_PATH = false;
		if($LAST_PATH && $LAST_PATH == trim($path,'/')){return;}

		$LAST_PATH = trim($path,'/'); // 一次请求只处理一次
		$driver = IO::init($path);
		if($driver->pathParse['type'] != KodIO::KOD_SOURCE) return;
		
		if(isset($_SERVER['HTTP_RANGE'])){ // 分片下载, 不是从头开始不再记录;
			$match = preg_match('/bytes=\s*(\d+)-(\d*)[\D.*]?/i',$_SERVER['HTTP_RANGE'],$matches);
			if($match && $matches[1] && intval($matches[1]) < 10){return;}
		}

		$sourceInfo = $this->model->sourceInfo($driver->pathParse['id']);
		if(!$sourceInfo || $sourceInfo['isDelete'] == '1' || $sourceInfo['isFolder'] == '1' || $sourceInfo['size'] == 0){return;}
		if($sourceInfo['targetType'] != SourceModel::TYPE_USER && $sourceInfo['targetType'] != SourceModel::TYPE_GROUP){return;}
		$this->model->where(array('sourceID'=>$sourceInfo['sourceID']))->save(array('viewTime' => time()));
		//write_log($this->in['URLrouter'].'; path='.$path.'; range='.($matches ? $matches[1]:''),'test');
	}
	
	//通用保存
	public function fileSave(){
		if(!$this->in['path'] || !$this->in['path']) return; 
		$result = IO::setContent($this->in['path'],$this->in['content']);
		Hook::trigger("explorer.fileSaveStart",$this->in['path']);
		show_json($result,!!$result);
	}
	//通用预览
	public function fileView(){
	}

	//通用缩略图
	public function fileThumb(){
		Hook::trigger("explorer.fileThumbStart",$this->in['path']);
	}	
}
lightApp.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/lightApp.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerLightApp extends Controller{
	private $model;
	function __construct()    {
		$this->model = Model('SystemLightApp');
		parent::__construct();
	}

	/**
	 * 获取列表
	 * 通过分类获取;默认为all
	 */
	public function get() {
		$group = Input::get('group','require','all');
		$list  = $this->model->listData(false,'id');
		$result = array();
		foreach ($list as $item) {
			if($item['group'] == $group || $group == 'all'){
				$result[] = $item;
			}
		}
		show_json($result);
	}

	/**
	 * 添加
	 */
	public function add() {
		$res = $this->model->add($this->input());
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.repeatError');
		show_json($msg,!!$res);
	}

	/**
	 * 编辑
	 */
	public function edit() {
		$name = $this->in['beforeName'];
		$res  = $this->model->update($name,$this->input());
		$msg = !!$res ? LNG('explorer.success') : LNG('explorer.repeatError');
		show_json($msg,!!$res);
	}
	/**
	 * 删除
	 */
	public function del() {
		$name = rawurldecode($this->in['name']);
		$res = $this->model->remove($name);
		$msg = !!$res ? LNG('explorer.success') : LNG('common.notExists');
		show_json($msg,!!$res);
	}
	
	public function getUrlContent(){
		$url = $this->in['url'];
		$header = url_header($url);
		if(!$header){show_json(array());}
		$contentType = $header['all']['content-type'];
		if(is_array($contentType)){$contentType = $contentType[count($contentType) - 1];}
		
		if(strstr($contentType,'text/html')){
			$content = curl_get_contents($url,30);
			$charset = get_charset($content);
			if($charset !='' && $charset !='utf-8' && function_exists("mb_convert_encoding")){
				$content = @mb_convert_encoding($content,'utf-8',$charset);
			}
			show_json(array('html'=>$content,'header'=>$header));
		}
		// 图片等处理;
		if(strstr($contentType,'image')){
			$content = curl_get_contents($url,30);
			show_json(array("content"=>base64_encode($content),'isBase64'=>true,'header'=>$header),true);
		}
		show_json(array('header'=>$header));
	}
	private function input(){
		$arr  = json_decode($this->in['data'],true);
		if(!is_array($arr)){
			show_json(LNG('explorer.error'),false);
		}
		return $arr;
	}
	
	 /**
     * 轻应用列表初始化
     */
    public function initApp(){
		$this->clearOldApps();
		$str  = file_get_contents(BASIC_PATH.'data/system/apps.php');
		$data = json_decode(substr($str, strlen('<?php exit;?>')),true);
		$data = is_array($data) ? $data : array();
		$data = array_reverse($data);
		foreach ($data as $app) {
			$type = $app['type'] == 'app' ? 'js' : $app['type'];
			$item = array(
				'name' 		=> $app['name'],
				'group'		=> $app['group'],
				'desc'		=> $app['desc'],
				'content'	=>  array(
					'type'		=> $type,
					'value'		=> $app['content'],
					'icon'		=> $app['icon'],
					'options' => array(
						"width"  	=> $app['width'],
						"height" 	=> $app['height'],
						"simple" 	=> $app['simple'],
						"resize" 	=> $app['resize']
					),
				)
			);
			if(isset($app['openType'])){
				$item['content']['options']['openType'] = $app['openType'];
			}
			if( $this->model->findByName($item['name']) ){
				$this->model->update($item['name'],$item);
			}else{
				$this->model->add($item);
			}
        }
    }
	private function clearOldApps(){
		// $this->model->clear();
		$clearOld = array(
			"豆瓣电台","365日历",
			'Kingdom Rush','Vector Magic','中国象棋','天气',"iqiyi影视",
			'计算器','音悦台','黑8对决','Web PhotoShop','一起写office',
			"微信","百度DOC",'百度随心听',"腾讯canvas","pptv直播","搜狐影视",
		);
		foreach($clearOld as $app){
			$this->model->remove($app);
		}
	}
}
list.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/list.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 文件列表通用入口获取
 * 基本数据: current/folderList/fileList/pageInfo;
 * 
 * 其他参数
 * listTypeSet 		// 指定列表模式; icon,list,split
 * listTypePhoto	// 强制显示图片模式
 * disableSort 		// 是否禁用排序; 0,1
 * pageSizeArray	// 自定义分页数量选择;
 * folderTips		// 目录警告提示信息;
 * groupShow 		// 分组依据;
 */
class explorerList extends Controller{
	private $model;
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	public function path($thePath = false){
		$path     = $thePath ? $thePath : $this->in['path'];
		$path     = $path != '/' ? rtrim($path,'/') : '/';//路径保持统一;
		$path	  = $path == '{io:systemRecycle}' ? IO_PATH_SYSTEM_RECYCLE:$path;
		$path 	  = $this->checkDesktop($path);
		$pathParse= KodIO::parse($path);
		$id 	  = $pathParse['id'];

		$current  = $this->pathCurrent($path);
		$this->checkExist($current,$path);
		switch($pathParse['type']){
			case KodIO::KOD_USER_FAV:			$data = Action('explorer.fav')->get();break;
			case KodIO::KOD_USER_FILE_TAG:		$data = Action('explorer.tag')->listSource($id);break;
			case KodIO::KOD_USER_RECYCLE:		$data = $this->model->listUserRecycle();break;
			case KodIO::KOD_USER_FILE_TYPE:		$data = Action('explorer.listFileType')->get($id,$pathParse);break;
			case KodIO::KOD_USER_RECENT:		$data = Action('explorer.listRecent')->listData();break;
			case KodIO::KOD_GROUP_ROOT_SELF:	$data = Action('explorer.listGroup')->groupSelf($pathParse);break;
			case KodIO::KOD_USER_SHARE:			$data = Action('explorer.userShare')->myShare('to');break;
			case KodIO::KOD_USER_SHARE_LINK:	$data = Action('explorer.userShare')->myShare('link');break;
			
			case KodIO::KOD_USER_SHARE_TO_ME:	$data = Action('explorer.userShare')->shareToMe($id);break;
			case KodIO::KOD_SHARE_ITEM:			$data = Action('explorer.userShare')->sharePathList($pathParse);break;
			case KodIO::KOD_SEARCH:				$data = Action('explorer.listSearch')->listSearch($pathParse);break;
			case KodIO::KOD_BLOCK:				$data = Action('explorer.listBlock')->blockChildren($id);break;
			case KodIO::KOD_SHARE_LINK:
			case KodIO::KOD_SOURCE:
			case KodIO::KOD_IO:
			default:$data = IO::listPath($path);break;
		}
		if($data === false){ // 获取失败情况处理;
			if($thePath){return false;}
			return show_json(IO::getLastError(LNG('explorer.error')),false);
		}
		$this->parseData($data,$path,$pathParse,$current);
		Action('explorer.listView')->listDataSet($data);

		if($thePath) return $data;
		show_json($data);
	}
	public function parseData(&$data,$path,$pathParse){
		$this->parseAuth($data,$path,$pathParse);
		$this->pageParse($data);
		$this->parseDataHidden($data,$pathParse);
		
		//回收站追加物理/io回收站;
		Action('explorer.recycleDriver')->appendList($data,$pathParse);
		Action('explorer.listGroup')->appendChildren($data);
		Action('explorer.listSafe')->appendSafe($data);
		Action('explorer.listPassword')->appendSafe($data);
		
		$this->pathListParse($data);// 1000 => 50ms; all-image=200ms;
		$this->pageReset($data);
		$this->addHistoryCount($data,$pathParse);
	}
	
	// 拉取所有(用于文件夹同步,对比等情况)
	// IO::listAll($this->in['path']);// path:默认为真实路径;包含sourceInfo时sourceInfo.path为真实路径;
	public function listAll(){
		$page		= isset($this->in['page']) ? intval($this->in['page']):1;
		$pageNum 	= isset($this->in['pageNum']) ? intval($this->in['pageNum']):2000;
		$page = $page <= 1 ? 1:$page;$pageNum = $pageNum <= 1000 ? 1000:$pageNum;		

		$list = IO::listAllSimple($this->in['path'],0);// path:包含上层文件夹名的路径;filePath:真实路径;
		$data = array_page_split($list,$page,$pageNum);
		show_json($data);
	}
	
	// 桌面文件夹自动检测;不存在处理;
	private function checkDesktop($path){
		if(!defined('MY_DESKTOP')) return $path;
		if(trim($path,'/') !== trim(MY_DESKTOP,'/')) return $path;
		if(IO::info($path)) return MY_DESKTOP;//存在则不处理;
		
		$desktopName = LNG('explorer.toolbar.desktop');
		$model  = Model("Source");
		$find   = IO::fileNameExist(MY_HOME,$desktopName);
		$rootID = KodIO::sourceID(MY_HOME);
		if(!$find){
			$find = $model->mkdir($rootID,$desktopName);
		}
		$model->metaSet($find,'desktop','1');
		$model->metaSet($rootID,'desktopSource',$find);
		Model('User')->cacheFunctionClear('getInfo',USER_ID);
		return KodIO::make($find);
	}
	
	public function pageParse(&$data){
		if(isset($data['pageInfo'])) return;
		$in = $this->in;
		$pageNumMax = 50000;
		$pageNum = isset($in['pageNum']) ? $in['pageNum'] : $pageNumMax;
		if($pageNum === -1){ // 不限分页情况; webdav列表处理;
			unset($in['pageNum']);
			$pageNumMax = 1000000;
			$pageNum = $pageNumMax;
		}

		$fileCount  = count($data['fileList']);
		$folderCount= count($data['folderList']);
		$totalNum	= $fileCount + $folderCount;
		$pageNum 	= intval($pageNum);
		$pageNum	= $pageNum <= 5 ? 5 : ($pageNum >= $pageNumMax ? $pageNumMax : $pageNum);
		$pageTotal	= ceil( $totalNum / $pageNum);
		$page		= intval( isset($in['page'])?$in['page']:1);
		$page		= $page <= 1 ? 1  : ($page >= $pageTotal ? $pageTotal : $page);
		$data['pageInfo'] = array(
			'totalNum'	=> $totalNum,
			'pageNum'	=> $pageNum,
			'page'		=> $page,
			'pageTotal'	=> $pageTotal,
		);
		if($pageTotal <= 1) return;

		$sort = $this->_parseOrder();
		$isDesc = $sort['desc'] == 'desc';
		$data['fileList'] 	= KodSort::arraySort($data['fileList'],$sort['key'],$isDesc,'name');
		$data['folderList'] = KodSort::arraySort($data['folderList'],$sort['key'],$isDesc,'name');
		
		$start = ($page-1) * $pageNum;
		$end   = $start + $pageNum;
		if( $end <= $folderCount){ // 文件夹范围内;
			$data['folderList'] = array_slice($data['folderList'],$start,$pageNum);
			$data['fileList'] 	= array();
		}else if($start >= $folderCount){ // 文件范围内;
			$data['folderList'] = array();
			$data['fileList'] 	= array_slice($data['fileList'],$start-$folderCount,$pageNum);
		}else{ // 各自占一部分;
			$folderNeed  = $folderCount - $start;
			$data['folderList'] = array_slice($data['folderList'],$start,$folderNeed);
			$data['fileList'] 	= array_slice($data['fileList'],0,$pageNum-($folderNeed) );
		}
	}
	private function pageReset(&$data){
		// 合并额外current数据;
		if(is_array($data['currentFieldAdd'])){
			$data['current'] = is_array($data['current']) ? $data['current'] : array();
			$data['current'] = array_merge($data['current'],$data['currentFieldAdd']);
			unset($data['currentFieldAdd']);
		}
		
		if(!isset($data['pageInfo'])) return;
		$group = isset($data['groupList']) ? count($data['groupList']) : 0;
		$total = count($data['fileList']) + count($data['folderList']) + $group;
		$pageInfo = $data['pageInfo'];
		if(	$pageInfo['page'] == 1 && $pageInfo['pageTotal'] == 1){
			$data['pageInfo']['totalNum'] = $total;
		}

		// 某一页因为权限全部过滤掉内容, 则加大每页获取条数;
		if(	$total == 0 && $pageInfo['totalNum'] != 0 && $pageInfo['pageTotal'] > 1 ){
			$this->in['pageNum'] = $pageInfo['pageNum'] * 2;
			if($this->in['pageNum'] < 500){
				$this->in['pageNum'] = 500;
			}
			$newData = $this->path($this->in['path']);
			show_json($newData);
		}
	}
	
	// 文件历史版本数量追加;
	private function addHistoryCount(&$data,$pathParse){
		if($pathParse['type'] == KodIO::KOD_SHARE_LINK) return;
		$sourceArr = array();$pathArr = array();
		foreach ($data['fileList'] as $file){
			if($file['sourceID']){
				// 修改时间小于等于创建时间时无历史版本; 忽略判断, 上传文件保留最后修改时间;
				// if($file['modifyTime'] <= $file['createTime']) continue; 
				$sourceArr[]  = $file['sourceID'];
			}
			if(!$file['sourceID'] && $file['isWriteable']){$pathArr[] = $file['path'];}
		}

		$countSource = $sourceArr ? Model("SourceHistory")->historyCount($sourceArr):array();
		$countLocal  = $pathArr   ? IOHistory::historyCount($pathArr):array();
		// if(!$countSource && !$countLocal) return;
		foreach ($data['fileList'] as $key=>$file){
			$data['fileList'][$key]['historyCount'] = 0; //默认设置为0
			if($file['sourceID'] && $countSource[$file['sourceID']]){
				$data['fileList'][$key]['historyCount'] = intval($countSource[$file['sourceID']]);
			}
			if($countLocal[$file['path']]){
				$data['fileList'][$key]['historyCount'] = intval($countLocal[$file['path']]);
			}
		}
	}
	public function fileInfoAddHistory($pathInfo){
		if(!$pathInfo || $pathInfo['type'] != 'file') return;
		
		$pathParse = KodIO::parse($pathInfo['path']);
		$data = array('fileList'=>array($pathInfo));
		$this->addHistoryCount($data,$pathParse);
		return $data['fileList'][0];
	}
	
	private function _parseOrder(){
		$defaultField = Model('UserOption')->get('listSortField');
		$defaultSort  = Model('UserOption')->get('listSortOrder');
		$sortTypeArr  = array('up'=>'asc','down'=>'desc');
		$sortFieldArr = array(
			'name'			=> 'name',
			'size'			=> 'size',
			'type'			=> 'ext',
			'ext'			=> 'fileType',
			'createTime'	=> 'createTime',
			'modifyTime'	=> 'modifyTime'
		);
		$sortField    = Input::get("sortField",'in',$defaultField,array_keys($sortFieldArr));
		$sortType	  = Input::get("sortType", 'in',$defaultSort,array_keys($sortTypeArr));
		if( !in_array($sortField,array_keys($sortFieldArr)) ){
			$sortField = 'name';
		}
		if( !in_array($sortType,array_keys($sortTypeArr)) ){
			$sortField = 'up';
		}
		return array('key'=>$sortFieldArr[$sortField],'desc'=>$sortTypeArr[$sortType]);
	}
	
	/**
	 * 检查目录是否存在;
	 */
	private function checkExist($current,$path){
		if(trim($path,'/') == '{source:0}') return;
		if(!$current || $current['exists'] === false){
			show_json(LNG('common.pathNotExists'),false);
		}
		$fromDav = _get($GLOBALS,'requestFrom') == 'webdav';
		if(isset($current['isDelete']) && $current['isDelete'] == '1' && !$fromDav){
			show_json(LNG("explorer.pathInRecycle"),false);
		}
	}

	public function pathCurrent($path,$loadInfo = true){
		$pathParse = KodIO::parse($path);
		try{// 收藏或访问的io路径不存在情况报错优化;
			$driver = IO::init($path); 
		}catch(Exception $e){
			$current = array('type'=>'folder','path'=>$path,'exists'=>false);
			return $current;
		}
		
		if($pathParse['isTruePath']){
			$current = array('type'=>'folder','path'=>$path);
			if(!$driver) {$current['exists'] = false;}
			if($driver && $loadInfo){
				$current = IO::info($path);
				$current = Model('SourceAuth')->authOwnerApply($current);
			}
			if($driver && !$loadInfo && $driver->getType() == 'local'){
				$currentInfo = IO::info($path);
				if($currentInfo){$current=$currentInfo;}
				if(!$currentInfo){$current['exists'] = false;}
			}
			return $current;
		}

		$listBlock = Action('explorer.listBlock');
		$current = $listBlock->ioInfo($pathParse['type']);
		if($pathParse['type'] == KodIO::KOD_BLOCK){
			$list = $listBlock->blockItems();
			$current = $list[$pathParse['id']];
			$current['name'] = _get($current,'name','root');
			$current['icon'] = 'block-'.$pathParse['id'];
		}else if($pathParse['type'] == KodIO::KOD_USER_FILE_TYPE){
			$list = Action('explorer.listFileType')->block();
			$current = $list[$pathParse['id']];
			$current['name'] = LNG('common.fileType').' - '.$current['name'];
		}else if($pathParse['type'] == KodIO::KOD_USER_FILE_TAG){
			$list = Action('explorer.tag')->tagList();
			$current = $list[$pathParse['id']];
			$current['name'] = LNG('explorer.userTag.title').' - '.$current['name'];
		}
		$current['type'] = 'folder';
		$current['path'] = $path;
		return $current;
	}
	
	public function pathListParse(&$data){
		$timeNow = timeFloat();
		$timeMax = 2.5;
		$infoFull= true;
		$data['current'] = $this->pathInfoParse($data['current'],$data['current']);
		foreach ($data as $type =>&$list) {
			if(!in_array($type,array('fileList','folderList','groupList'))) continue;
			foreach ($list as $key=>&$item){
				if(timeFloat() - $timeNow >= $timeMax){$infoFull = false;}
				$data[$type][$key] = $this->pathInfoParse($item,$data['current'],$infoFull);
			};unset($item);
		};unset($list);
		$data = Hook::filter('explorer.list.path.parse',$data);
	}
	
	public function pathInfoParse($pathInfo,$current=false,$infoFull=true){
		if(!$pathInfo) return false;
		static $showMd5 		= false; // 大量文件夹文件内容时,频繁调用性能优化;
		static $explorerFav 	= false;
		static $explorerTag 	= false;
		static $explorerShare 	= false;
		static $explorerTagGroup= false;
		static $explorerDriver 	= false;
		static $modelAuth 		= false;
		if(!$explorerFav){
			$explorerFav 		= Action('explorer.fav');
			$explorerTag 		= Action('explorer.tag');
			$explorerShare 		= Action('explorer.userShare');
			$explorerTagGroup 	= Action('explorer.tagGroup');
			$explorerDriver 	= Action('explorer.listDriver');
			$explorerDriver 	= Action('explorer.listDriver');
			$modelAuth 			= Model('Auth');
			$showMd5 			= Model('SystemOption')->get('showFileMd5') != '0';
		}

		if(USER_ID){
			$explorerFav->favAppendItem($pathInfo);
			$explorerTag->tagAppendItem($pathInfo);
			$explorerShare->shareAppendItem($pathInfo);
			$explorerTagGroup->tagAppendItem($pathInfo);
		}
		if($infoFull){
			if( substr($pathInfo['path'],0,4) == '{io:'){
				$explorerDriver->parsePathIO($pathInfo,$current);
			}
			if($pathInfo['type'] == 'folder' && !isset($pathInfo['hasFolder']) ){
				$explorerDriver->parsePathChildren($pathInfo,$current);
			}
			if($pathInfo['type'] == 'file' && !$pathInfo['_infoSimple']){
				$this->pathParseOexe($pathInfo);
				$this->pathInfoMore($pathInfo);
			}
		}else{
			if($pathInfo['type'] == 'folder'){
				$pathInfo['hasFolder'] = true;
				$pathInfo['hasFile']   = true;
			}
		}
		if(!$pathInfo['pathDisplay']){$pathInfo['pathDisplay'] = $pathInfo['path'];}

		// 下载权限处理;
		if(!array_key_exists('isTruePath',$pathInfo)){
			$pathInfo['isTruePath'] = KodIO::isTruePath($pathInfo['path']);
		}
		$pathInfo['canDownload'] = $pathInfo['isTruePath'];
		if(isset($pathInfo['auth'])){
			$pathInfo['canDownload'] = $modelAuth->authCheckDownload($pathInfo['auth']['authValue']);
		}

		// 写入权限;
		if($pathInfo['isTruePath']){
			if(isset($pathInfo['auth'])){
				$pathInfo['canWrite'] = $modelAuth->authCheckEdit($pathInfo['auth']['authValue']);
			}
			if(is_array($pathInfo['metaInfo']) && 
				isset($pathInfo['metaInfo']['systemLock']) && 
				$pathInfo['metaInfo']['systemLock'] != USER_ID ){
				$pathInfo['isWriteable'] = false;
			}
		}
		if($pathInfo['type'] == 'file' && !$pathInfo['ext']){
			$pathInfo['ext'] = strtolower($pathInfo['name']);
		}
		$pathInfo = $this->pathInfoCover($pathInfo);
		
		// 没有下载权限,不显示fileInfo信息;
		if(!$pathInfo['canDownload'] || !$showMd5){
			if(isset($pathInfo['fileInfo'])){unset($pathInfo['fileInfo']);}
			if(isset($pathInfo['hashMd5'])){unset($pathInfo['hashMd5']);}
		}
		if(isset($pathInfo['fileID'])){unset($pathInfo['fileID']);}
		if(isset($pathInfo['fileInfo']['path'])){unset($pathInfo['fileInfo']['path']);}
		return $pathInfo;
	}
	
	public function pathInfoCover($pathInfo){
		// 文件文件夹封面; 自适应当前url;
		if(is_array($pathInfo['metaInfo']) && $pathInfo['metaInfo']['user_sourceCover']){
			$pathInfo['fileThumbCover'] = '1';
			$pathInfo['fileThumb'] = Action('user.view')->parseUrl($pathInfo['metaInfo']['user_sourceCover']);
		}
		if($pathInfo['type'] == 'file'){ // 仅针对文件; 追加缩略图等业务;
			$pathInfo = Hook::filter('explorer.list.itemParse',$pathInfo);
		}
		return $pathInfo;
	}

	/**
	 * 递归处理数据;自动加入打开等信息
	 * 如果是纯数组: 处理成 {folderList:[],fileList:[],thisPath:xxx,current:''}
	 */
	private function parseAuth(&$data,$path,$pathParse){
		if( !isset($data['folderList']) || 
			!is_array($data['folderList'])
		) { //处理成统一格式
			$listTemp = isset($data['fileList']) ? $data['fileList'] : $data;
			$data = array(
				"folderList" 	=> $listTemp ? $listTemp : array(),
				'fileList'		=> array()
			);
		}
		if(!is_array($data['fileList'])){$data['fileList'] = array();}
		if(!is_array($data['folderList'])){$data['folderList'] = array();}
		$path = rtrim($path,'/').'/';
		if(!isset($data['current']) || !$data['current']){
			$data['current']  = $this->pathCurrent($path);
		}
		$data['thisPath'] = $path;
		if(is_array($data['current']) && $data['current']['path']){
			$data['thisPath'] = rtrim($data['current']['path'],'/').'/';
		}
		if(!$data['targetSpace']){
			$data['targetSpace'] = $this->targetSpace($data['current']);
		}
		foreach ($data['folderList'] as &$item) {
			if( isset($item['children']) ){
				$item['isParent'] = true;
				$pathParseParent = KodIO::parse($item['path']);
				$this->parseAuth($item['children'],$item['path'],$pathParseParent);
			}
			$item['type'] = isset($item['type']) ? $item['type'] : 'folder';
		};unset($item);
		if($pathParse['type'] == KodIO::KOD_SHARE_LINK) return;

		$data['fileList']   = $this->dataFilterAuth($data['fileList']);
		$data['folderList'] = $this->dataFilterAuth($data['folderList']);
		
		// 列表处理;
		switch($pathParse['type']){
			case KodIO::KOD_USER_FAV:
			case KodIO::KOD_USER_RECENT:
			case KodIO::KOD_GROUP_ROOT_SELF:
			case KodIO::KOD_BLOCK:
				$data['disableSort'] = 1;// 禁用客户端排序;
				// $data['listTypeSet'] = 'list'; //强制显示模式;
				break;
			default:break;
		}
	}
	
	// 显示隐藏文件处理; 默认不显示隐藏文件;
	private function parseDataHidden(&$data,$pathParse){
		if(Model('UserOption')->get('displayHideFile') == '1') return;
		$pathHidden = Model('SystemOption')->get('pathHidden');
		$pathHidden = explode(',',$pathHidden);
		$hideNumber = 0;

		if($pathParse['type'] == KodIO::KOD_USER_SHARE_TO_ME) return;
		foreach ($data as $type =>$list) {
			if(!in_array($type,array('fileList','folderList'))) continue;
			$result = array();
			foreach ($list as $item){
				$firstChar = substr($item['name'],0,1);
				if($firstChar == '.' || $firstChar == '~') continue;
				if(in_array($item['name'],$pathHidden)) continue;
				$result[] = $item;
			}
			$data[$type] = $result;
			$hideNumber  += count($list) - count($result);
		}
	}

	
	// 用户或部门空间尺寸;
	public function targetSpace($current){
		if(!_get($current,'targetID')) return false;
		if(	isset($current['auth']) &&
			$current['auth']['authValue'] == -1 ){
			return false;
		}
		if(!$current || !isset($current['targetType'])){
			$current = array("targetType"=>'user','targetID'=>USER_ID);//用户空间;
		}
		return Action('explorer.auth')->space($current['targetType'],$current['targetID']);
	}
	
	private function dataFilterAuth($list){
		if($list && Action('explorer.auth')->allowRootSourceInfo($list[0])) return $list;
		$shareLinkPre = '{shareItemLink';
		foreach ($list as $key => $item) {
			if( substr($item['path'],0,strlen($shareLinkPre)) == $shareLinkPre) continue;
			if( isset($item['targetType']) &&
				$item['targetType'] == 'user' &&
				$item['targetID'] == USER_ID ){
				continue;
			}
			// if(!isset($item['auth'])) continue;
			if( isset($item['targetType']) && 
				(!$item['auth'] || $item['auth']['authValue'] == 0 ) // 不包含-1,构建通路;
			){
				unset($list[$key]);
			}
		}
		if(!is_array($list)) return array();
		return array_values($list);
	}

	// 文件详细信息处理;
	public function pathInfoMore(&$pathInfo){
		if(!GetInfo::support($pathInfo['ext'])) return $pathInfo;
		if($pathInfo['targetType'] == 'system') return $pathInfo;
		
		$infoKey  = 'fileInfoMore';
		$cacheKey = md5($pathInfo['path'].'@'.$pathInfo['size'].$pathInfo['modifyTime']);
		$fileID   = _get($pathInfo,'fileID');
		$fileID   = _get($pathInfo,'fileInfo.fileID',$fileID);
		if(!isset($pathInfo['sourceID'])){
			$infoMore = Cache::get($cacheKey);
			if(is_array($infoMore)){$pathInfo[$infoKey] = $infoMore;}
		}
		
		// 没有图片尺寸情况,再次计算获取;[更新]
		$isImage  = in_array($pathInfo['ext'],array('jpg','jpeg','png','ico','bmp'));
		if($isImage && !isset($pathInfo[$infoKey]['sizeWidth'])){
			unset($pathInfo[$infoKey]);
			if(!isset($pathInfo['sourceID'])){Cache::remove($cacheKey);} //不使用缓存;
		}
		
		$debug = KodUser::isRoot() && $this->in['debug'] == '1'; // 调试模式,直接立即获取;
		if($debug){
			unset($pathInfo[$infoKey]);Cache::remove($cacheKey);//debug
			if($fileID){Model("File")->metaSet($fileID,$infoKey,null);};
			$infoMore = $this->pathInfoMoreParse($pathInfo['path'],$cacheKey,$fileID);
			if(is_array($infoMore)){$pathInfo[$infoKey] = $infoMore;}
		}
		
		// 异步延迟获取;
		$fileHash = $fileID ? $fileID : $cacheKey;
		if(!isset($pathInfo[$infoKey]) || $pathInfo[$infoKey]['etag'] != $fileHash){
			$args = array($pathInfo['path'],$cacheKey,$fileID);// 异步任务处理;
			$desc = '[pathInfoMore]'.$pathInfo['name'];
			$key  ='pathInfoMoreParse-'.($fileID ? $fileID : $cacheKey);
			TaskQueue::add('explorer.list.pathInfoMoreParse',$args,$desc,$key);
		}
		
		// md5 未计算情况处理;(队列加入失败,执行退出或文件不存在等情况补充)
		if($fileID && is_array($pathInfo['fileInfo']) && !$pathInfo['fileInfo']['hashMd5']){
			$fileInfo = Model("File")->fileInfo($fileID);
			$args = array($fileID,$fileInfo);
			$desc = '[fileMd5]'.$fileID.';path='.$pathInfo['path'];
			TaskQueue::add('FileModel.fileMd5Set',$args,$desc,'fileMd5Set'.$fileID);
		}
		
		// 文件封面;
		if(isset($pathInfo[$infoKey]) && isset($pathInfo[$infoKey]['fileThumb']) ){
			$fileThumb = $pathInfo[$infoKey]['fileThumb'];
			if(!IO::exist($fileThumb)){ // 不存在检测处理;
				unset($pathInfo[$infoKey]);Cache::remove($cacheKey);
				if($fileID){Model("File")->metaSet($fileID,$infoKey,null);};
				return $pathInfo;
			}
			unset($pathInfo[$infoKey]['fileThumb']);
			$pathInfo['fileThumb'] = Action('explorer.share')->linkFile($fileThumb);
		}
		return $pathInfo;
	}
	
	// 解析文件详情;
	public function pathInfoMoreParse($file,$cacheKey,$fileID=false){
		$infoKey  = 'fileInfoMore';
		$infoFull = IO::info($file);
		unset($infoFull[$infoKey]);
		GetInfo::infoAdd($infoFull);
		$infoMore = isset($infoFull[$infoKey]) ? $infoFull[$infoKey]:false;
		
		if(!$infoMore) return;
		$infoMore['etag'] = $fileID ? $fileID : $cacheKey;
		if($fileID){
			Model("File")->metaSet($fileID,$infoKey,json_encode($infoMore));
		}else{
			Cache::set($cacheKey,$infoMore,3600*24*30);
		}
		return $infoMore;
	}
	
	// 获取文件内容, 存储在对象存储时不存在处理; 避免报错
	private function pathGetContent($pathInfo){
		if(!$pathInfo || !$pathInfo['path']){return "";}
		$filePath = $pathInfo['path'];
		if(isset($pathInfo['fileID'])){
			$fileInfo = Model("File")->fileInfo($pathInfo['fileID']);
			if(!$fileInfo || !$fileInfo['path']){return "";}
			$filePath = $fileInfo['path'];
		}
		if(!IO::exist($filePath)){return "";}
		return IO::getContent($filePath);
	}
		
	/**
	 * 追加应用内容信息;
	 */
	private function pathParseOexe(&$pathInfo){
		$maxSize = 1024*1024*1;
		if($pathInfo['ext'] != 'oexe' || $pathInfo['size'] > $maxSize) return $pathInfo;
		if($pathInfo['size'] == 0) return $pathInfo;
		if(!isset($pathInfo['oexeContent'])){
			// 文件读取缓存处理; 默认缓存7天;
			$pathHash = KodIO::hashPath($pathInfo);
			$content  = Cache::get($pathHash);
			if(!$content){
				$content = $this->pathGetContent($pathInfo);
				Cache::set($pathHash,$content,3600*24*7);
			}
			if(!is_string($content) || !$content) return $pathInfo;
			$pathInfo['oexeContent'] = json_decode($content,true);
		}
				
		if( $pathInfo['oexeContent']['type'] == 'path' && 
			isset($pathInfo['oexeContent']['value']) ){
			$linkPath = $pathInfo['oexeContent']['value'];
			$parse = KodIO::parse($pathInfo['path']);
			$parsePath = KodIO::parse($linkPath);
			if($parse['type'] == KodIO::KOD_SHARE_LINK) return $pathInfo;
			if(!$parsePath['isTruePath']){return $pathInfo;}
			
			if(Action('explorer.auth')->fileCan($linkPath,'show')){
				if(substr($linkPath,0,4) == '{io:'){ //io路径不处理;
					$infoTarget = array('path'=>$linkPath);
					$infoTarget = Action('explorer.listDriver')->parsePathIO($infoTarget);
				}else{
					$infoTarget = IO::info($linkPath);
				}
				$pathInfo['oexeSourceInfo'] = $infoTarget;
			}
		}
		return $pathInfo;
	}
}
listBlock.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listBlock.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 文件列表通用入口获取
 */
class explorerListBlock extends Controller{
	private $model;
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	
	/**
	 * 数据块数据获取
	 */
	public function blockChildren($type){
		$result = array();
		switch($type){
			case 'root':		$result = $this->blockRoot();break; //根
			case 'files': 		$result = $this->blockFiles();break;
			case 'tools': 		$result = $this->blockTools();break;
			case 'safe': 		$result = Action('explorer.listSafe')->listRoot();break;
			case 'fileType': 	$result = Action('explorer.listFileType')->block();break;
			case 'fileTag': 	$result = Action('explorer.tag')->tagList();break;
			case 'driver': 		$result = Action("explorer.listDriver")->get();break;
		}
		if(!is_array($result)) return array();
		if(isset($result['folderList'])) return $result;
		return array_values($result);
	}
	
	/**
	 * 根数据块
	 */
	private function blockRoot(){
		$list = $this->blockItems();
		if(!$this->pathEnable('fileType')){unset($list['fileType']);}
		if(!KodUser::isRoot() || !$this->pathEnable('driver')){unset($list['driver']);}
		if(!$this->pathEnable('fileTag')){unset($list['fileTag']);}
		$result = array();
		foreach ($list as $type => $item) {
			$block = array_merge($item,array(
				"path"		=> '{block:'.$type.'}/',
				"isParent"	=> true,
			));
			if($block['open'] || $block['children']){
				$block['children'] = $this->blockChildren($type);
				if($block['children'] === false) continue;
			} // 必须有children,没有children的去除(兼容Android <= 2.15)
			$result[] = $block;
		}
		return $result;
	}
	public function blockItems(){
		$list = array(
			'files'		=> array('name'=>LNG('common.position'),'open'=>true), 
			'tools'		=> array('name'=>LNG('common.tools'),'open'=>true),
			'fileType'	=> array('name'=>LNG('common.fileType'),'open'=>false,'children'=>true,'pathDesc'=> LNG('explorer.pathDesc.fileType')),
			'fileTag'	=> array('name'=>LNG('explorer.userTag.title'),'open'=>false,'children'=>true,'pathDesc'=> LNG('explorer.pathDesc.tag')),
			'driver'	=> array('name'=>LNG('common.mount').' (admin)','open'=>false,'pathDesc'=> LNG('explorer.pathDesc.mount')),
		);
		return $list;
	}
	
	private function groupRoot(){
		$groupArray = Action('filter.userGroup')->userGroupRootShow();
	    if (!$groupArray || empty($groupArray[0])) return false;
	    return Model('Group')->getInfo($groupArray[0]);
	}
	
	/**
	 * 文件位置
	 * 收藏夹、我的网盘、公共网盘、我所在的部门
	 */
	private function blockFiles(){
		$groupInfo 	= $this->groupRoot();
		$list = array(
			"fav"		=> array("path"	=> KodIO::KOD_USER_FAV,'pathDesc'=>LNG('explorer.pathDesc.fav')),
			"my"		=> array(
				'name' 			=> LNG('explorer.toolbar.rootPath'),//我的网盘
				'sourceRoot' 	=> 'userSelf',//文档根目录标记;前端icon识别时用:用户,部门
				"path"			=> KodIO::make(Session::get('kodUser.sourceInfo.sourceID')),
				'open'			=> true,
				'pathDesc'		=> LNG('explorer.pathDesc.home')
			),
			"rootGroup"	=> array(
				'name' 			=> $groupInfo['name'],
				'sourceRoot' 	=> 'groupPublic',
				"path"			=> KodIO::make($groupInfo['sourceInfo']['sourceID']),
				'pathDesc'		=> LNG('explorer.pathDesc.groupRoot')
			),
			"myGroup"	=> array("path"	=> KodIO::KOD_GROUP_ROOT_SELF,'pathDesc'=>LNG('explorer.pathDesc.myGroup')),
			'shareToMe'	=> array("path"	=> KodIO::KOD_USER_SHARE_TO_ME),
		);
		$option = Model('SystemOption')->get();
		if(!$this->pathEnable('myFav')){unset($list['fav']);}
		if(!$this->pathEnable('my')){unset($list['my']);}
		if(!$this->pathEnable('rootGroup') || !$groupInfo || !$groupInfo['sourceInfo']){unset($list['rootGroup']);}
		if(!$this->pathEnable('myGroup')){unset($list['myGroup']);}
		if($option['groupSpaceLimit'] == '1' && $option['groupSpaceLimitLevel'] <= 1){
			unset($list['myGroup']);
		}
		
		
		// 根部门没有权限,且没有子内容时不显示;
		if(isset($list['rootGroup'])){
			$rootChildren = Action('explorer.list')->path($list['rootGroup']['path']);
			$hasAuth = _get($rootChildren,'current.auth.authValue');
			if(!$rootChildren['pageInfo']['totalNum'] && $hasAuth <= 0){
				unset($list['rootGroup']);
			}
		}
		
		// 没有所在部门时不显示;
		if(isset($list['myGroup'])){
			$selfGroup 	= Session::get("kodUser.groupInfo");
			$groupArray = array_to_keyvalue($selfGroup,'','groupID');//自己所在的组
			$group 		= array_remove_value($groupArray,$groupInfo['groupID']);
			if(!$group && !isset($list['rootGroup'])){unset($list['myGroup']);}
			if(!$selfGroup){unset($list['myGroup']);}
		}

		$explorer = Action('explorer.list');
		$result = array();
		foreach ($list as $pathItem){
			$item = $explorer->pathCurrent($pathItem['path']);
			if(!$item) continue;			
			$item['isParent'] = true;
			if($item['open']){ //首次打开:默认展开的路径,自动加载字内容
				$item['children'] = $explorer->path($item['path']);
			}			
			$result[] = array_merge($item,$pathItem);
		}
		return $result;
	}
	
	public function pathEnable($type){
		$model  = Model('SystemOption');
		$option = $model->get();
		if( !isset($option['treeOpen']) ) return true;
		
		// 单独添加driver情况;更新后处理;  单独加入文件类型开关,则根据flag标记;自动处理;
		// my,myFav,myGroup,rootGroup,recentDoc,fileType,fileTag,userPhoto,driver
		$checkType = array(
			'treeOpenMy' 		=> 'my',
			'treeOpenMyGroup' 	=> 'myGroup',
			'treeOpenFileType' 	=> 'fileType',
			'treeOpenFileTag' 	=> 'fileTag',
			'treeOpenRecentDoc' => 'recentDoc',
			
			'treeOpenPhoto' 	=> 'userPhoto',
			'treeOpenDriver' 	=> 'driver',
			'treeOpenFav'		=> 'myFav',
			'treeOpenRootGroup'	=> 'rootGroup',
		);
		foreach ($checkType as $keyType=>$key){
			if(isset($GLOBALS['TREE_OPTION_IGNORE']) && $GLOBALS['TREE_OPTION_IGNORE'] == '1') break;
			if( $option[$keyType] !='ok'){
				$model->set($keyType,'ok');
				$model->set('treeOpen',$option['treeOpen'].','.$key);
				$result = true;
				$option = $model->get();
			}
		}
		if($result) return true;
		
		$allow = explode(',',$option['treeOpen']);
		return in_array($type,$allow);
	}
		
	/**
	 * 工具
	 */
	private function blockTools(){
		$list = $this->ioInfo(array(
			KodIO::KOD_USER_RECENT,
			KodIO::KOD_USER_SHARE,
			KodIO::KOD_USER_SHARE_LINK,'userPhoto',
			KodIO::KOD_USER_RECYCLE,
		));
		if(!$this->pathEnable('recentDoc')){
			unset($list[KodIO::KOD_USER_RECENT]);
		}
		if(!$this->pathEnable('userPhoto')){
			unset($list['userPhoto']);
		}
		if(Model('UserOption')->get('recycleOpen') == '0'){
			unset($list[KodIO::KOD_USER_RECYCLE]);
		}
		if(Model('SystemOption')->get('shareLinkAllow') == '0'){
			unset($list[KodIO::KOD_USER_SHARE_LINK]);
		}
		if(!is_array($list)) return array();
		return array_values($list);
	}

	public function ioInfo($pick){
		$list = array(
			array(KodIO::KOD_USER_FAV,'explorer.toolbar.fav','explorer.pathDesc.fav'),
			array(KodIO::KOD_GROUP_ROOT_SELF,'explorer.toolbar.myGroup','explorer.pathDesc.myGroup'),
			array(KodIO::KOD_USER_RECENT,'explorer.toolbar.recentDoc','explorer.pathDesc.recentDoc'),
			array(KodIO::KOD_USER_SHARE,'explorer.toolbar.shareTo','explorer.pathDesc.shareTo'),
			array(KodIO::KOD_USER_SHARE_LINK,'explorer.toolbar.shareLink','explorer.pathDesc.shareLink'),
			array(KodIO::KOD_USER_SHARE_TO_ME,'explorer.toolbar.shareToMe',''),
			array(KodIO::KOD_USER_RECYCLE,'explorer.toolbar.recycle','explorer.pathDesc.recycle'),
			array(KodIO::KOD_SEARCH,'common.search',''),
			array('userPhoto',LNG('explorer.toolbar.photo'),LNG('explorer.photo.desc'),'{userFileType:photo}/'),
		);
		$result = array();
		foreach ($list as $item){
			$thePath = isset($item[3]) ? $item[3]:$item[0];
			$result[$item[0]] = array(
				"name"		=> LNG($item[1]),
				"path"		=> $thePath.'/',
				"pathDesc"	=> $item[2] ? LNG($item[2]) : '',
			);
		}
		if(is_string($pick)){
			return $result[$pick];
		}else if(is_array($pick)){
			$pickArr = array();
			foreach ($pick as $value) {
				$pickArr[$value] = $result[$value];
			}
			return $pickArr;
		}		
		return $result;	
	}
}
listDriver.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listDriver.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
class explorerListDriver extends Controller{
	public function __construct(){
		parent::__construct();
	}
	
	/**
	 * 用户存储挂载列表
	 */
	public function get(){
		if(KodUser::isRoot()) return $this->rootList();
		return false;//普通用户挂载暂不支持;
	}
	
	
	private function rootList(){
		$GLOBALS['STORE_WITH_SIZEUSE'] = true;
		$dataList = Model('Storage')->listData();
		unset($GLOBALS['STORE_WITH_SIZEUSE']);
		$list = array();
		
		if($GLOBALS['config']['systemOption']['systemListDriver'] == '1'){
			$diskList = KodIO::diskList(false);
			foreach ($diskList as $path) {
				$this->driverMake($list,$path);
			}
		}
		foreach ($dataList as $item) {
			$list[] = array(
				"name"			=> $item['name'],
				"path"			=> '{io:'.$item['id'].'}/',
				"size"			=> $item['sizeUse'],
				"driverSpace"	=> $item['sizeMax']*1024*1024*1024,
				"driverDefault" => $item['default'],
				"driverType"	=> $item['driver'],
				"icon" 			=> 'io-'.strtolower($item['driver']),
				'isParent'		=> true,
			);
		}
		$result = array('folderList' => $list, 'fileList'=>array());
		$this->driverListGroup($result);
		return $result;
	}
	
	// 对同一类型有多个存储的进行归类;
	private function driverListGroup(&$result){
		$notEqual  = array('!='=>'1');
		$groupShow = array(
			array(
				'type' 	=> 'io-type-default',
				'title' => LNG('admin.storage.current'),
				"filter"=> array('driverDefault'=>array('='=>'1')),
			)
		);
		if(count($result['folderList']) <= 5){
			$groupShow[] = array(
				'type' 	=> 'io-type-others',
				'title' => LNG('common.others'),
				"filter"=> array('driverDefault'=>$notEqual),
			);
			$result['groupShow'] = $groupShow;
			return;
		}
		
		$groupMinNumber = 3; // 超过数量才显示分组,否则归类到其他;
		$driverOthers   = array();
		$listGroup = array_to_keyvalue_group($result['folderList'],'driverType');
		foreach ($listGroup as $key=>$val){
			if(count($val) < $groupMinNumber){
				$driverOthers[] = $key;continue;
			}
			$langKey = 'admin.storage.'.strtolower($key);
			$groupShow[] = array(
				'type' 	=> 'io-type-'.$key,
				'title' => LNG($langKey) != $langKey ? LNG($langKey) : $key,
				"filter"=> array('ioDriver'=>array('='=> $key),'driverDefault'=>$notEqual),
			);
		}
		if(count($driverOthers) > 0){
			$groupShow[] = array(
				'type' 	=> 'io-type-others',
				'title' => LNG('common.others'),
				"filter"=> array('ioDriver'=>array('in'=>$driverOthers),'driverDefault'=>$notEqual),
			);
		}
		$result['groupShow'] = $groupShow;
	}
	
	public function parsePathIO(&$info,$current=false){
		if(substr($info['path'],0,4) != '{io:') return $info;
		static $driverList = false;
		if ($driverList === false) {
			$list = Model('Storage')->driverListSystem();
			$driverList = array_to_keyvalue($list,'id');
		}
		if(!$driverList) return $info;
		
		$isFavPath = false;
		if(is_array($current) && isset($current['path'])){
			$isFavPath = trim($current['path'],'/') == KodIO::KOD_USER_FAV;
		}
		
		$parse = KodIO::parse($info['path']);
		$storage = $driverList[$parse['id']];
		if(!$storage) return $info;

		$storageName = str_replace("/",'-',$storage['name']);
		$info['isReadable']   = array_key_exists('isReadable',$info)  ? $info['isReadable']  : true;
		$info['isWriteable']  = array_key_exists('isWriteable',$info) ? $info['isWriteable'] : true;
		$info['pathDisplay']  = str_replace($parse['pathBase'],$storageName,$info['path']);
		$langKey = 'admin.storage.'.strtolower($storage['driver']);
		$info['ioType'] = LNG($langKey) != $langKey ? LNG($langKey) : $storage['driver'];
		$info['ioDriver'] = $storage['driver'];
		$info['ioIsSystem'] = (isset($storage['default']) && $storage['default'] == 1);
		
		// 系统目录不允许写操作; 暂时屏蔽;
		if($info['ioIsSystem'] && !GLOBAL_DEBUG){
			if( preg_match("/{io:\d+}\/20\d\d[0-1][0-9]($|\/)/",$info['path'],$match) || 
				preg_match("/{io:\d+}\/database($|\/)/i",$info['path'],$match)
			){
				$info['isReadable']   = false;
				$info['isWriteable']  = false;
			}
		}

		// 根目录;
		$thePath = trim($parse['param'],'/');
		if( (!$thePath && $thePath !== '0') || $isFavPath ){
			$info['name'] = $storageName;
			if($isFavPath){
			    $info['name'] = $info['sourceInfo']['favName'];
			}
			$info['icon'] = 'io-'.strtolower($storage['driver']);
			if(isset($storage['config']['domain'])){
				$info['ioDomain'] = $storage['config']['domain'];
			}
			if(isset($storage['config']['bucket'])){
				$info['ioBucket'] = $storage['config']['bucket'];
			}
			if(isset($storage['config']['basePath'])){
				$info['ioBasePath'] = $storage['config']['basePath'];
			}
		}
		// pr($storage,$parse,$info,$driverList);exit;
		return $info;
	}
	public function parsePathChildren(&$info,$current){
		if($info['type'] == 'file' || isset($info['hasFolder']) ) return $info;	
		$ioAllow = array('Local');// array('Local','MinIO')
		$pathParse = KodIO::parse($current['path']);
		$isLocal = $pathParse['type'] ? false:true;
		$isIoAllow = isset($current['ioType']) && in_array($current['ioType'],$ioAllow);
		if($pathParse['type'] == KodIO::KOD_BLOCK && $pathParse['id'] != 'driver') return $info;

		$infoMore = array('hasFolder'=>true,'hasFile'=> true);
		if($isLocal || $isIoAllow){
			$infoMore = IO::has($info['path'],1);
		}else if($pathParse['type'] == KodIO::KOD_USER_FAV){
			$itemParse = KodIO::parse($info['path']);
			if(!$itemParse['type']){
				$infoPath = IO::info($info['path']);
				if(!$infoPath){
					$infoMore = array('exists'=>false);
				}else{
					$infoMore = IO::has($info['path'],1);
					$infoMore = is_array($infoMore) ? $infoMore : array();
					$infoMore = array_merge($infoPath,$infoMore);
				}
			}
		}
		if(is_array($infoMore)){
			unset($infoMore['name']);
			$info = array_merge($info,$infoMore);
		}
		return $info;
	}

	private function driverMake(&$list,$path){
		if(!file_exists($path)) return;
		if(!function_exists('disk_total_space')){return;}
		$total  = @disk_total_space($path);
		$list[] = array(
			"name"			=> LNG('admin.storage.driver')."($path)",
			"path"			=> $path,
			"size"			=> $total - @disk_free_space($path),
			"driverSpace"	=> $total,
			"driverType"	=> 'Local',
			"ioType" 		=> LNG("admin.storage.driver"),
			"ioDriver"		=> "Local",			
			"icon" 			=> 'io-driver',
			'isParent'		=> true,
		);
	}
}
listFileType.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listFileType.class.php'
View Content
<?php
/**
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
class explorerListFileType extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	
	/**
	 * 文件类型列表
	 */
	public function block(){
		$docType = KodIO::fileTypeList();
		$list	 = array();
		foreach ($docType as $key => $value) {
			$list[$key] = array(
				"name"		=> $value['name'],
				"path"		=> KodIO::makeFileTypePath($key),
				'ext'		=> $value['ext'],
				'extType'	=> $key,
			);
		}
		return $list;
	}
	
	public function get($type,$pathParse){
		if($type == 'photo') return $this->getPhoto($pathParse);
		return $this->model->listPathType($type);
	}
	
	private function getPhoto($pathParse){
		$pageNum = $this->in['pageNum'];
		$data = $this->listData($pathParse);
		$this->groupData($data,$pageNum);
			
		// 设置了分页时按路径保存在本地,不记录服务端; 默认500
		$data['pageSizeArray'] 	= array(100,200,500,1000,2000,5000); 
		$data['disableSort'] 	= 1;	// 禁用客户端排序;
		$data['listTypePhoto']  = 1; 	// 强制显示图片模式;
		$data['listTypeSet'] 	= 'icon'; 	// 强制显示模式;
		return $data;
	}
		
	// 默认个人空间; 可扩展到任意文件夹相册模式;
	private function listData($pathParse){
		$search   = Action('explorer.listSearch');
		$sizeFrom = 100;
		$homePath = MY_HOME;
		$fileType = 'jpg,jpeg,png,gif,bmp,heic,webp,mov,mp4';// livp;
		$option   = json_decode(Model("UserOption")->get('photoConfig'),true);
		if(is_array($option)){
			if(isset($option['pathRoot'])){$homePath = $option['pathRoot'];}
			if(isset($option['fileSize'])){$sizeFrom = intval($option['fileSize']) * 1024;}
			if(isset($option['fileType'])){$fileType = $option['fileType'];}
		}

		$thePath = rtrim($pathParse['param'],'/');
		$thePath = $thePath ? $thePath : $homePath;
		if(substr($thePath,0,2) == '/{'){$thePath = substr($thePath,1);}
		if(!Action('explorer.auth')->fileCanRead($thePath)){
			$errorTips = LNG('explorer.noPermissionAction'); //指定目录无权限或不存在处理;
			$destInfo  = IO::info($thePath);
			if(!$destInfo || $destInfo['pathType'] == "{systemRecycle}"){
				$errorTips = LNG('common.pathNotExists');
			}
			$result = array("fileList"=>array(),'folderList'=>array());
			$result['current'] = $this->photoCurrent($pathParse,$thePath);
			$result['folderTips'] = $errorTips;
			show_json($result,true);
		}
		
		$this->in['pageNum'] = 20000; // 最多查询数量;
		$param  = array('parentPath'=>$thePath,'fileType'=>$fileType);
		if($sizeFrom > 0){$param['sizeFrom'] = $sizeFrom;}
		$param['parentID']  = $search->searchPathSource($thePath);
		$result = $search->searchData($param);
		$result['current'] = $this->photoCurrent($pathParse,$thePath);
		return $result;
	}
	private function photoCurrent($pathParse,$thePath){
		$option   	 = json_decode(Model("UserOption")->get('photoConfig'),true);
		$thePath     = rtrim($pathParse['param'],'/');
		$pathAddress = array(array('name' =>LNG('explorer.toolbar.photo'),'path'=>$pathParse['path']));
		$pathDesc    = LNG('explorer.photo.desc');
		if($thePath){
			$thePath = substr($thePath,0,2) == '/{' ? ltrim($thePath,'/') : $thePath;
			$info = IO::info($thePath);
			$pathAddress[0]['name'] = LNG('explorer.toolbar.folder').' - '.$info['name'];
			$pathAddress[] = array('name' =>trim($info['pathDisplay'],'/'),'path'=>$thePath);
			$pathDesc = $info['pathDisplay'] ? $info['pathDisplay']: $info['path'];
		}else if(is_array($option)){
			if(isset($option['pathRootShow'])){$pathDesc .= '<br/>'.LNG('explorer.photo.pathRoot').': '.$option['pathRootShow'];}
		}
		if(is_array($option)){
			if(isset($option['fileType'])){$pathDesc .= '<br/>'.LNG('explorer.photo.fileType').': '.$option['fileType'];}
		}
		
		$result = array(
			'path' 		=> $pathParse['path'],
			'name' 		=> $pathAddress[0]['name'],
			'pathDesc' 	=> $pathDesc,
			'type' 		=> 'folder',
			'pathAddress' => $pathAddress,
		);
		return $result;
	}
	
	
	// 数据分组
	private function groupData(&$data,$pageNum){
		$this->resetImageTime($data);
		$fileList 	= array_sort_by($data['fileList'],'imageTime',true);

		$groupBy 	= Input::get('photoListBy','in','month',array('year','month','day'));// 分组类型
		$pageNum 	= intval($pageNum) <= 100 ? 100 : intval($pageNum);
		$pageTotal  = ceil(count($fileList) / $pageNum);
		$page 		= isset($this->in['page']) ? intval($this->in['page']) : 1;
		$page 		= $page <= 1 ? 1 : ($page >= $pageTotal ? $pageTotal : $page); 

		$groupArray = array();
		$listPage   = array_slice($fileList,$pageNum * ($page - 1),$pageNum);
		$groupTypeArr = array(
			'year'  => array('Y','-01-01 00:00:00',' +1 year'),
			'month' => array('Y-m','-01 00:00:00', ' +1 month'),
			'day'   => array('Y-m-d',' 00:00:00',  ' +1 day'),
		);
		$groupType = $groupTypeArr[$groupBy];
		
		foreach($listPage as $file){
			$key = date($groupType[0],$file['imageTime']);
			if(!isset($groupArray[$key])){
				$timeStart = strtotime($key.$groupType[1]);
				$timeTo    = strtotime(date('Y-m-d H:i:s',$timeStart).$groupType[2]);				
				$groupArray[$key] = array(
					'type' 	=> 'photo-group-'.$timeStart,
					'title' => $key,
					"desc"  => '', 'count'=> 0,
					"filter"=> array('imageTime'=>array('>'=> $timeStart,'<'=> $timeTo)),
				);
			}
			$groupArray[$key]['count'] += 1;
			$groupArray[$key]['desc']   = $groupArray[$key]['count'] .' '. LNG('common.items');
		}
		// pr($groupArray,$page,$pageTotal);exit;
		$data['fileList']  = $listPage;
		$data['groupShow'] = array_values($groupArray);
		$data['pageInfo']  = array('page'=>$page,'pageTotal'=>$pageTotal,'totalNum'=>count($fileList),'pageNum'=>$pageNum);
	}
	
	// 图片时间处理, 优先级: 拍摄时间>本地最后修改时间>上传时间
	public function resetImageTime(&$data){
		foreach($data['fileList'] as &$file){
			$file['imageTime'] = intval($file['modifyTime']);
			if(is_array($file['fileInfoMore']) && isset($file['fileInfoMore']['createTime'])){
				$file['imageTime'] = strtotime($file['fileInfoMore']['createTime']);
				if($file['imageTime']){continue;}
			}
			if(is_array($file['metaInfo']) && isset($file['metaInfo']['modifyTimeLocal'])){
				$file['imageTime'] = intval($file['metaInfo']['modifyTimeLocal']);
			}
			$file['imageTime'] = intval($file['modifyTime']);
		};unset($file);
	}
}
listGroup.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listGroup.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
class explorerListGroup extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
		$this->modelGroup = Model('Group');
	}

	public function groupSelf($pathInfo){//获取组织架构的用户和子组织;
		$groupArray = Session::get("kodUser.groupInfo");
		$groupArray = array_sort_by($groupArray,'groupID');
		$groupArray = $this->groupSelfLimit($groupArray);
		$listData 	= $this->groupArray($groupArray);
		$this->groupSelfAppendAllow($listData);
		return $listData;
	}
	
	// 我所在的部门, 罗列自己有权限的部门(通路)
	public function groupSelfAppendAllow(&$listData){
		if(intval($this->in['page']) > 1) return;// 第一页才罗列;
		if(isset($this->in['fromType']) && $this->in['fromType']=='tree'){return;} // 树目录不显示,仅在文件区域显示;
		$groupArray = Action('filter.userGroup')->userGroupRootShow();
	    if(!$groupArray || empty($groupArray[0])) return false;

		$groupList = array();$groupListArr = array();
		foreach ($listData as $key => $item) {
			$item['sourceRootSelf'] = 'self';
			$groupList[] = $item;
			$groupListArr[$item['targetID']] = $item;
			unset($listData[$key]);
		}
				
		$groupChild = $this->modelGroup->where(array('parentID'=>$groupArray[0]))->select();
		$groupAdd 	= $this->groupArray($groupChild);
		$groupAddTo = array();// 去除重复已在该部门的部门;
		foreach ($groupAdd as $item){
			if(isset($groupListArr[$item['targetID']])){continue;}
			$groupAddTo[] = $item;
		}
		
		$listData['groupList'] = $groupList;
		$listData['folderList']= $groupAddTo;
		$desc = '('.LNG('explorer.toolbar.myGroupAllowDesc').')';
		$listData['groupShow'] = array(
			array('type'=>'childGroupSelf', 'title'=>LNG('explorer.toolbar.myGroup'),"filter"=>array('sourceRootSelf'=>'self')),
			array('type'=>'childGroupAllow','title'=>LNG('explorer.toolbar.myGroupAllow'),"desc"=>$desc,"filter"=>array('sourceRootSelf'=>'!=self')),
		);
		if(count($listData['folderList']) == 0){unset($listData['groupShow']);}
	}
	
	// 部门层级限制处理; 超过层级限制的部门,展示该部门在限制层级时的部门通路;
	private function groupSelfLimit($groupArray){
		$option = Model('SystemOption')->get();
		if($option['groupSpaceLimit'] != '1') return $groupArray;
		
		$levelMax = intval($option['groupSpaceLimitLevel']);
		$groupMap = array_to_keyvalue($groupArray,'groupID');
		$groupArray = array();
		foreach ($groupMap as $groupID => $item) {
			$level = explode(',',trim($item['parentLevel'],',')); // -1 + 1(去掉0;加上自己; 最后一层是自己)
			if(count($level) <= $levelMax){$groupArray[$groupID] = $item;continue;}

			$autoGroupID = $level[$levelMax];
			if(!isset($groupMap[$autoGroupID])){
				$info = $this->modelGroup->getInfoSimple($autoGroupID);
				$groupMap[$autoGroupID] = array(
					'groupID'		=> $info['groupID'],
					'groupName'		=> $info['name'],
					'parentLevel'	=> $info['parentLevel']
				);
			}
			if(!is_array($groupMap[$autoGroupID])){continue;}
			$groupArray[$autoGroupID] = $groupMap[$autoGroupID];
		}
		// pr($groupMap,$groupArray);exit;
		return $groupArray;
	}
	
	// 是否允许罗列部门的子部门;
	private function enableListGroup($groupID,&$data){
		$option = Model('SystemOption')->get();
		if($option['groupSpaceLimit'] == '1'){
			$groupInfo = $this->modelGroup->getInfoSimple($groupID);
			$level = explode(',',trim($groupInfo['parentLevel'],',')); // -1 + 1(去掉0;加上自己)
			if(count($level) >= intval($option['groupSpaceLimitLevel'])){return false;}
		}
		
		if( !isset($option['groupListChild']) ) return true;
		
		// 仅左侧树目录罗列子部门,不罗列文件文件夹; 文件区域不罗列子部门,仅罗列文件文件夹;
		// app 忽略树目录设定; 编辑请求忽略树目录设定;
		if($option['groupListChild'] == '2'){
			$device = Action('filter.userCheck')->getDevice();
			if($device && $device['type'] == 'app'){return true;} // app忽略此设定;
			if(isset($this->in['listTree']) && $this->in['listTree'] == 'all'){return true;}
			
			if($this->in['fromType'] == 'tree'){
				$data['folderList'] = array();$data['fileList']   = array();
				return true;
			}			
			return false;
		}
				
		$listGroup = $option['groupListChild']=='1';
		if(!$listGroup) return false;
		if($groupID == '1'){
			return is_null($option['groupRootListChild']) || $option['groupRootListChild']=='1';
		}
		return true;
	}
	
	// 根据多个部门信息,构造部门item;
	private function groupArray($groupArray){
		$groupArray = array_sort_by($groupArray,'sort');// 排序处理;
		$groupArray	= array_to_keyvalue($groupArray,'groupID');//自己所在的组
		$this->_filterDisGroup($groupArray);	// 过滤已禁用部门
		$group = array_keys($groupArray);
		if(!$group) return array();

		// 部门目录是否显示子部门; 0 不显示;1 全部显示;2=仅树目录显示
		$isFromTree    = isset($this->in['fromType']) && $this->in['fromType']=='tree';
		$groupListType = Model('SystemOption')->get('groupListChild');
		if(isset($this->in['listTree']) && $this->in['listTree'] == 'all'){
			$isFromTree = false;$groupListType = '1';
		}
		
		$groupSource = $this->model->sourceRootGroup($group);
		$groupSource = array_to_keyvalue($groupSource,'targetID');
		$result = array();
		foreach($groupArray as $group){ // 保持部门查询结构的顺序;
			$groupID = $group['groupID'];
			if($groupID == '1'){// 去除根部门(未禁用显示企业网盘时去除)
				if(Action('explorer.listBlock')->pathEnable('rootGroup')){continue;}
			}
			if(!isset($groupSource[$groupID])) continue;
			
			$groupInfo = Model('Group')->getInfo($groupID);
			$pathInfo  = $groupSource[$groupID];
			$pathInfo['sourceRoot'] = 'groupPath';
			$pathInfo['hasGroup']   = $groupInfo ? $groupInfo['hasChildren']:0;
			$pathInfo['pathDisplay']= $pathInfo['groupPathDisplay'];
			if(!$pathInfo['auth']){
				$pathInfo['auth'] = Model("SourceAuth")->authDeepCheck($pathInfo['sourceID']);
			}
			
			// 部门根目录是否有文件夹取决于[有文件夹,或有子部门]; 后台设置--仅树目录显示子部门时,是否可展开取决于是否有子部门;
			$pathInfo['hasFolder'] = $pathInfo['hasFolder'] || $pathInfo['hasGroup'];
			if($isFromTree && $groupListType == '2'){
				$pathInfo['hasFolder'] = $pathInfo['hasGroup'];
				$pathInfo['hasFile'] = false;
			}
			
			$userRootShow = KodUser::isRoot() && $GLOBALS['config']["ADMIN_ALLOW_SOURCE"];
			if(!$userRootShow){
				if( !$pathInfo['auth'] || $pathInfo['auth']['authValue'] == 0){ // 放过-1; 打开通路;
					continue;// 没有权限;
				}
			}
			$result[] = $pathInfo;
		}
		// pr($result,$groupSource,$group,$groupArray);exit;
		return $result;
	}
	// 过滤已禁用部门
	private function _filterDisGroup(&$list){
		if(empty($list)) return array();
		$where = array(
			'groupID'	=> array('in', array_keys($list)),
			'key'		=> 'status', 
			'value'		=> 0
		);
		$data = Model('group_meta')->where($where)->field('groupID')->select();
		foreach($data as $value) {
			unset($list[$value['groupID']]);
		}
	}
	
	/**
	 * 部门根目录;罗列子部门;
	 */
	public function appendChildren(&$data){
		$pathInfo = $data['current'];
		if(!$pathInfo || _get($pathInfo,'targetType') != 'group') return false;
		if(isset($pathInfo['shareID'])) return false;
		if(intval($this->in['page']) > 1) return;// 第一页才罗列;

		//不是根目录
		$parents = $this->model->parentLevelArray($pathInfo['parentLevel']);
		if(count($parents) != 0) return false;
		if(!$this->enableListGroup($pathInfo['targetID'],$data)) return;
		
		$groupList  = $this->modelGroup->where(array('parentID'=>$pathInfo['targetID']))->select();
		$groupListAdd  = $this->groupArray($groupList);
		$data['pageInfo']['totalNum'] += count($groupListAdd);

		$data['groupList'] = $groupListAdd;
		$data['groupShow'] = array(
			array('type'=>'childGroup','title'=>LNG('explorer.pathGroup.group'),"filter"=>array('sourceRoot'=>'groupPath')),
			array('type'=>'childContent','title'=>LNG('explorer.pathGroup.groupContent'),"filter"=>array('sourceRoot'=>'!=groupPath')),
		);
		if(count($data['groupList']) == 0){unset($data['groupShow']);}
		// show_json($data);exit;
	}

	public function pathGroupAuthMake($groupID,$userID=false){
		$groupRoot  = '1';
		$groupInfo  = $this->modelGroup->getInfoSimple($groupID);//101
		if(!$userID){
			$groupSelf 	= Session::get("kodUser.groupInfo");
		}else{
			$userInfo = Model('User')->getInfo($userID);
			$groupSelf = $userInfo['groupInfo'];
		}
		if(!$groupSelf) return false;
		
		// 部门文件夹或子文件夹没有针对自己设置权限,向上级部门回溯;
		$groupSelf 	= array_to_keyvalue($groupSelf,'groupID');//自己所在的组
		$parents	= $this->model->parentLevelArray($groupInfo['parentLevel']);
		$parents[]	= $groupID;
		$parents 	= array_reverse($parents);
		foreach ($parents as $id) {
			if($id == $groupRoot) return false;// 根部门;
			if(isset($groupSelf[$id])){
				return array(
					'authValue' => intval($groupSelf[$id]['auth']['auth']),
					'authInfo'  => $groupSelf[$id]['auth'],
				);
			}
		}
		return Model("SourceAuth")->authDeepCheck($groupInfo['sourceInfo']['sourceID'],$userID);
	}

	/**
	 * 用户根目录,部门根目录操作检测
	 * 不允许: 重命名,删除,复制,剪切,下载,分享
	 */
	public function pathRootCheck($action){
		$disable = array(
			'path' 	=> array(
				'explorer.index.pathRename',
				'explorer.userShare.add',
				'explorer.userShare.get',
				'explorer.userShare.edit',
			),
			'dataArr'=> array(
				'explorer.index.pathDelete',
				'explorer.index.pathCopy',
				'explorer.index.pathCute',
				'explorer.index.pathCopyTo',
				'explorer.index.pathCuteTo',
				'explorer.index.zipDownload'
			),
		);
		foreach ($disable as $type=>$medhods) {
			$disable[$type] = array();
			foreach ($medhods as $item) {
				$disable[$type][] = strtolower($item);
			}
		}
		
		$allAction = array_merge($disable['path'],$disable['dataArr']);
		if(!in_array($action,$allAction)) return;
		
		$isGroupRoot = false;
		$errorAdd = '';
		if(in_array($action,$disable['path'])){
			$isGroupRoot = $this->pathIsRoot($this->in['path']);
		}else{
			$data = json_decode($this->in['dataArr'],true);
			if(is_array($data)){
				foreach ($data as $item) {
					$isGroupRoot = $this->pathIsRoot($item['path']);
					if($isGroupRoot){
						$errorAdd = '['.$item['name'].'],';
						break;
					}
				}
			}
		}
		if(!$isGroupRoot) return;
		return show_json($errorAdd.LNG('explorer.pathNotSupport'),false);
	}
	// 检测目录是否为部门根目录;
	private function pathIsRoot($path){
		$parse = KodIO::parse($path);
		if($parse['type'] != KodIO::KOD_SOURCE) return false;
		
		$info = IO::infoSimple($path);
		if($info['targetType'] != SourceModel::TYPE_GROUP) return false;
		if($info['targetType'] != SourceModel::TYPE_USER)  return false;
		if($info['parentID'] =='0') return true;//部门根目录,用户根目录;
		// if(!$info || $info['targetType'] != 'group') return false;
		return false;
	}
	
}
listPassword.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listPassword.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 加密文件夹处理
 */
class explorerListPassword extends Controller{
	private $model;
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	// 权限检测
	public function authCheck($pathInfo,$action){
		if(!$this->checkAuthNeed($pathInfo)){return;} // 拥有编辑以上权限则忽略;
		$passInfo = $this->checkAllowPassword($pathInfo);
		if($passInfo){// 当前文件,或上层需要密码;
			if($action == 'show'){return;}
			return LNG('explorer.folderPass.tips');
		}
		
		// 查询子目录是否包含需要设置密码的文件夹;
		if($pathInfo['type'] == 'folder' && $action == 'download'){
			$where = array(
				"source.parentLevel"=> array("like",$pathInfo['parentLevel'].$pathInfo['sourceID'].',%'),
				"source.isDelete"	=> 0,
				"meta.key"			=> 'folderPassword',
			);
			$field = "source.sourceID,meta.value";
			$join  = "LEFT JOIN io_source_meta meta on source.sourceID = meta.sourceID";
			$list  = $this->model->alias("source")->field($field)->where($where)->join($join)->select();
			$sourceMeta = $this->folderPasswordMeta(array_to_keyvalue($list,'','sourceID'));
			$needPassword = array();
			foreach($sourceMeta as $sourceID => $passInfo){
				$sessionKey = "folderPassword_".$passInfo['sourceID'];
				if( $passInfo && Session::get($sessionKey) != $passInfo['folderPassword']){
					$needPassword[] = $passInfo;
				}
			}
			// trace_log([$passInfo,$pathInfo,$action,$list,$needPassword]);
			$msgCount = ' ['.count($needPassword).' '.LNG('common.items').']';
			if($needPassword){return LNG('explorer.folderPass.tipsHas').$msgCount;}
		}
	}
	
	// 追加到根目录;
	public function appendSafe(&$data){
		$passInfo = $this->checkAllowPassword($data['current']);
		if(!$passInfo){return;}
		
		unset($passInfo['folderPassword']);
		$data['fileList']   = array();
		$data['folderList'] = array();
		$data['folderTips'] = LNG('explorer.folderPass.tips').";".htmlentities($passInfo['folderPasswordDesc']);
		$data['pathDesc']   = $data['folderTips'];
		$data['folderPasswordNeed'] = $passInfo;
	}
	
	private function checkAuthNeed($pathInfo){
		if(!$pathInfo || empty($pathInfo['sourceID']) || empty($pathInfo['parentLevel'])){return false;}
		$canEdit  = is_array($pathInfo['auth']) ? Model("Auth")->authCheckEdit($pathInfo['auth']['authValue']):false;
		$userSelf = $pathInfo['targetType'] == 'user' && $pathInfo['targetID'] == KodUser::id();
		if($userSelf || $canEdit || KodUser::isRoot()){return false;}
		return true;
	}
	private function checkAllowPassword($pathInfo){
		if(!$this->checkAuthNeed($pathInfo)){return false;}
		$passInfo = $this->folderPasswordFind($pathInfo);
		if(!$passInfo){return false;}
		
		$sessionKey = "folderPassword_".$passInfo['sourceID'];
		if( $passInfo && !empty($this->in['folderPassword']) && 
			$this->in['folderPassword'] == $passInfo['folderPassword']){
			Session::set($sessionKey,$passInfo['folderPassword']);
			$passInfo = false;// 输入密码正确;
		}
		if( $passInfo && Session::get($sessionKey) == $passInfo['folderPassword']){
			$passInfo = false;// 回话中存在密码,且一致;
		}
		return $passInfo;
	}
	
	
	// 向上查找上层文件夹包含密码设置的文件夹(上级多层包含密码时,仅匹配最近的一层密码设置; )
	private function folderPasswordFind($pathInfo){
		static $_cache = array();

		$keys = array('folderPassword','folderPasswordDesc','folderPasswordTimeTo','folderPasswordUser');
		$parentLevel = $this->model->parentLevelArray($pathInfo['parentLevel']);
		$parentLevel[] = $pathInfo['sourceID'];
		$parentLevel = array_reverse($parentLevel);
		$findHas = false;
		foreach($parentLevel as $sourceID){
			if(empty($_cache[$sourceID])){continue;}
			$findHas = $_cache[$sourceID];break;
		}
		if($findHas || isset($_cache[$pathInfo['sourceID']])){return $findHas;}
		
		$sourceMeta = $this->folderPasswordMeta($parentLevel);
		foreach ($sourceMeta as $sourceID => $meta) {
			$_cache[$sourceID] = $meta;
		}
		foreach($parentLevel as $sourceID){
			if(empty($_cache[$sourceID])){continue;}
			$findHas = $_cache[$sourceID];break;
		}
		return $findHas;
	}
	
	private function folderPasswordMeta($sourceArr){
		$sourceMeta = array();
		if(!$sourceArr){return $sourceMeta;}
		$keys 	 = array('folderPassword','folderPasswordDesc','folderPasswordTimeTo','folderPasswordUser');
		$where   = array('sourceID'=>array('in',$sourceArr),'key'=>array('in',$keys));
		$metaArr = Model('io_source_meta')->where($where)->select();
		$metaArr = array_to_keyvalue_group($metaArr,'sourceID');
		foreach($metaArr as $sourceID=>$theMeta){
			$meta = array_to_keyvalue($theMeta,'key','value');
			if(empty($meta['folderPassword'])){$meta = array();}
			if(!empty($meta['folderPasswordTimeTo']) && $meta['folderPasswordTimeTo'] < time() ){ //已过期处理;
				$meta = array();
			}
			if(!empty($meta['folderPasswordUser'])){
				$item['value'] = Model("User")->getInfoSimpleOuter($meta['folderPasswordUser']);
			}
			if(!empty($meta)){$meta['sourceID'] = $sourceID;}
			$sourceMeta[$sourceID] = $meta;
		}
		return $sourceMeta;
	}
	
}
listRecent.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listRecent.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
class explorerListRecent extends Controller{
	public function __construct(){
		$this->model = Model("Source");
		parent::__construct();
	}

	/**
	 * 最近文档;
	 * 仅限自己的文档;不分页;不支持排序;  最新修改时间 or 最新修改 or 最新打开 max top 100;
	 * 
	 * 最新自己创建的文件(上传or拷贝)
	 * 最新修改的,自己创建的文件	
	 * 最新打开的自己的文件 		
	 * 
	 * 资源去重;整体按时间排序【创建or上传  修改  打开】
	 */
	public function listData(){
		$list = array();
		$this->listRecentWith('createTime',$list);		//最近上传or创建
		$this->listRecentWith('modifyTime',$list);		//最后修改
		$this->listRecentWith('viewTime',$list);		//最后打开
		
		//合并重复出现的类型;
		foreach ($list as &$value) {
			$value['recentType'] = 'createTime';
			$value['recentTime'] = $value['createTime'];
			if($value['modifyTime'] > $value['recentTime']){
				$value['recentType'] = 'modifyTime';
				$value['recentTime'] = $value['modifyTime'];
			}
			if($value['viewTime'] > $value['recentTime']){
				$value['recentType'] = 'viewTime';
				$value['recentTime'] = $value['viewTime'];
			}
			$value['recentTime'] = intval($value['recentTime']);
		};unset($value);
		
		$list = array_sort_by($list,'recentTime',true);
		$listRecent = array_to_keyvalue($list,'sourceID');
		$result = array();
		if(!empty($listRecent)){
			$where  = array( 'sourceID'=>array('in',array_keys($listRecent)) );
			$result = $this->model->listSource($where);
		}
		$fileList = array_to_keyvalue($result['fileList'],'sourceID');
		
		//保持排序,合并数据
		foreach ($listRecent as $sourceID => &$value) {
			$item  = $fileList[$sourceID];
			if(!$item){
				unset($listRecent[$sourceID]);
				continue;
			}
			$value = array_merge($value,$item);
		}
		$result['fileList'] = array_values($listRecent);
		
		$todayStart = strtotime(date('Y-m-d 00:00:00'));
		$dayTime    = 24 * 3600;
		$result['groupShow'] = array(
			array(
				'type' 	=> 'time-today',
				'title' => LNG("common.date.today"),
				"desc"  => date("Y-m-d",time()),
				"filter"=> array('recentTime'=>array('>'=> $todayStart )),
			),
			array(
				'type'	=> 'time-yesterday',
				'title'	=> LNG("common.date.yestoday"),
				"desc"  => date("Y-m-d",$todayStart - $dayTime),
				"filter"=> array('recentTime'=>array('<'=> $todayStart,'>'=> $todayStart - $dayTime )),
			),
			array(
				'type'	=> 'time-before',
				'title'	=> LNG('common.date.before'),
				"filter"=> array('recentTime'=>array('<'=> $todayStart - $dayTime )),
			),
		);
		
		return $result;
	}
	private function listRecentWith($timeType,&$result){
		$userID = USER_ID;
		$userInfo = Model('User')->getInfo($userID);
		$where  = array(
			'targetType'	=> SourceModel::TYPE_USER,
			'targetID'		=> $userID,
			'parentLevel'	=> array("like",',0,'.$userInfo['sourceInfo']['sourceID'].',%'),
			'isFolder'		=> 0,
			'isDelete'		=> 0,
			'size'			=> array('>',0),
		);
		// 内部协作分享,修改上传内容; 普通用户可能没有权限进入处理;
		if(KodUser::isRoot()){
			if(in_array($timeType,array('createTime','modifyTime')) ){
				unset($where['targetID']);
				$where['targetType'] = array('in',array(SourceModel::TYPE_USER,SourceModel::TYPE_GROUP));
			}
			if($timeType == 'createTime'){$where['createUser'] = $userID;}
			if($timeType == 'modifyTime'){$where['modifyUser'] = $userID;}
		}
		$where[$timeType] = array(
			array('>',time() - 3600*24*60),//2个月内
			array('<',time()),// 忽略大于当前时间内容;
		);

		$maxNum = 50;	//最多150项
		$field  = 'sourceID,name,createTime,modifyTime,viewTime';
		$list   = $this->model->field($field)->where($where)
					->limit($maxNum)->order($timeType.' desc')->select();
		$list   = array_to_keyvalue($list,'sourceID');
		$result = array_merge($result,$list);
		$result = array_to_keyvalue($result,'sourceID');
	}
}
listSafe.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listSafe.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 私密保险箱处理(个人根目录置顶; 状态:未启用,启用-已授权/未授权)
 * 
 * 根目录权限:不支持:复制/剪切/重命名/拖拽; 
 * 子内容权限:不能外链分享/协作分享/收藏/添加标签/发送到桌面快捷方式; 不能通过用户根目录搜索;
 */
class explorerListSafe extends Controller{
	private $model;
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	// 权限检测
	public function authCheck($pathInfo,$action){
		if(!isset($pathInfo['sourceAt']) || $pathInfo['sourceAt'] != 'pathSafeSpace'){return;}

		$spaceInfo = $this->spaceInfo();
		if($spaceInfo['type'] != 'isLogin'){
			if(ACTION == 'explorer.list.path'){show_json($spaceInfo['listEmpty'],true);}
			return LNG('explorer.safe.isNotLogin');
		}
		$notAllowAction = array('share'); // 保险箱内内容不支持的操作: 分享编辑/删除;
		if(in_array($action,$notAllowAction)){return LNG('explorer.pathNotSupport');}
		return false;
	}
	
	// 保险箱内内容不支持的操作;sourceID 路径检测(主动调用)
	public function authCheckAllow($path){
		if(!is_numeric($path) || !$path){return;}
		$pathInfo = $this->model->pathInfo($path);
		if(!$pathInfo || $pathInfo['sourceAt'] != 'pathSafeSpace'){return;}
		show_json(LNG('explorer.pathNotSupport'),false);
	}
	
	// 追加到根目录;
	public function appendSafe(&$data){
		$this->appendSafeChildren($data);
		$this->appendSafeUserRootAdmin($data);
		$pathInfo = $data['current'];
		if(!$pathInfo || intval($this->in['page']) > 1)  return false;
		if(!isset($pathInfo['targetType']) || !isset($pathInfo['targetID']) || !isset($pathInfo['parentID'])){return;}
		if($pathInfo['parentID'] != 0 || $pathInfo['targetType'] != 'user' || $pathInfo['targetID'] != USER_ID) return;
		if(defined('KOD_FROM_WEBDAV')){return;}
		
		$userInfo = Model("User")->getInfoFull(USER_ID);
		if(_get($data,'current.sourceID') == _get($userInfo,'metaInfo.pathSafeFolder')) return;
		if(_get($data,'current.sourceID') != _get($userInfo,'sourceInfo.sourceID')) return;	// 仅个人空间根目录下显示
		if(Model("UserOption")->get('pathSafeSpaceShow') != '1'){return;} // 个人设置已隐藏;
		$spaceInfo = $this->spaceInfo();
		
		// 系统全局关闭,且自己未启用时不显示;
		if(Model("SystemOption")->get('pathSafeSpaceEnable') != '1'){
			if($spaceInfo['type'] == 'isNotOpen'){return;}
			// return;
		}
		$data['folderList'][] = $spaceInfo['folderRoot'];
	}

	// 私密空间子列表处理(未登录提示登录; 已登录子内容追加标记)
	private function appendSafeChildren(&$data){
		$pathInfo = $data['current'];
		
		// 私密保险箱根目录, 追加应用根目录入口;
		if($pathInfo['parentID'] == 0 && _get($pathInfo,'metaInfo.pathSafeFolderUser')){
			$userInfo = Model('User')->getInfoFull($pathInfo['targetID']);
			$userAppRoot = _get($userInfo,'metaInfo.pathAppRoot','');
			if($userAppRoot){
				$data['folderList'][] = array(
					'name'		=> LNG('explorer.appFolder'),
					'path'		=> '{source:'.$userAppRoot.'}/',
					'icon'		=> 'kod-box',//kod-box kod-folder2
					'type'		=> 'folder','pathReadOnly'=>true,
					'pathDesc'	=> LNG('explorer.appFolder'),
					'metaInfo' 	=> array('systemSort'=>3000000000,'systemSortHidden'=>true),
				);
			}
		}
		if(!isset($pathInfo['sourceAt']) || $pathInfo['sourceAt'] != 'pathSafeSpace'){return;}

		$spaceInfo = $this->spaceInfo();
		if($spaceInfo['type'] != 'isLogin'){ // 未登录访问拦截;
			return show_json($spaceInfo['listEmpty'],true);
		}
		foreach ($data['fileList'] as &$item){
			$item['sourceAt'] = 'pathSafeSpace';
		};unset($item);
		foreach ($data['folderList'] as &$item){
			$item['sourceAt'] = 'pathSafeSpace';
		};unset($item);		
	}
	
	// 系统管理员: 管理用户根目录--显示私密文件夹处理(默认关闭)
	private function appendSafeUserRootAdmin(&$data){
		if(!$this->config["ADMIN_ALLOW_USER_SAFE"]){return;}
		$pathInfo = $data['current'];
		if(!KodUser::isRoot() || !$pathInfo || !$pathInfo['sourceID']){return;}
		if($pathInfo['parentID'] != 0 || $pathInfo['targetType'] != 'user') return;
		if($pathInfo['targetID'] == USER_ID) return;//自己的根目录;
		$userInfo = $userInfo = Model("User")->getInfoFull($pathInfo['targetID']);
		$userSafeSpace = _get($userInfo,'metaInfo.pathSafeFolder');
		if(!$userSafeSpace){return;}
		
		$infoChange = array(
			'name'		=> LNG('explorer.safe.name'),
			'icon'		=> 'user-folder-safe',
			'metaInfo' 	=> array('systemSort'=>3000000000,'systemSortHidden'=>true),
			'sourceAt'	=> 'pathSafeSpace',
			'ownerUser'	=> '',
		);
		$sourceInfo = Model("Source")->pathInfo($userSafeSpace);
		if(!$sourceInfo){return;}

		// 该用户保险箱根目录;
		if($userSafeSpace == $pathInfo['sourceID']){
			$userRootName = LNG('common.user').'['.$userInfo['name'].']';
			$userRoot = KodIO::make(_get($userInfo,'sourceInfo.sourceID'));
			$infoChange['pathAddress'] = array(
				array("name"=> $userRootName,"path"=>$userRoot),
				array("name"=> LNG('explorer.safe.name'),"path"=>$sourceInfo['path']),
			);
			$data['current'] = array_merge($data['current'],$infoChange);
			return;
		}
		// 该用户保险箱子目录;
		$data['folderList'][] = array_merge($sourceInfo,$infoChange);
	}
	
	
	// 状态及数据处理; isNotOpen/isNotLogin/isLogin;
	private function spaceInfo(){
		static $result = false;
		if(is_array($result)) return $result;
		KodUser::checkLogin();
		
		$uesrID     = Session::get('kodUser.userID');
		$userInfo   = Model("User")->getInfoFull($uesrID);
		$safeFolder = _get($userInfo,'metaInfo.pathSafeFolder','');
		$isLogin 	= Session::get('pathSafe-userIsLogin') == '1' ? true : false;
		$type = !$safeFolder ? 'isNotOpen' : ($isLogin ? 'isLogin' : 'isNotLogin');
		$message = array(
			'isNotOpen' 	=> LNG('explorer.safe.isNotOpen'),
			'isNotLogin' 	=> LNG('explorer.safe.isNotLogin'),
			'isLogin' 		=> LNG('explorer.safe.isLogin'),
		);
		$result = array('type'=>$type,'folder'=>$safeFolder,'message'=>$message[$type]);
		$result['folderRoot'] = array(
			'name'	=> LNG('explorer.safe.name'),
			'path'	=> '{block:safe}/',
			'type'	=> 'folder',
			'pathDesc'	=> LNG('explorer.safe.desc'),
			'pathSafe'	=> $type,'pathReadOnly'=>true,
			'metaInfo' 	=> array('systemSort'=>3000000000,'systemSortHidden'=>true),
		);
		if($type != 'isLogin'){
			$result['folderRoot']['hasFile'] = false;
			$result['folderRoot']['hasFolder'] = false;
		}
		
		$result['listEmpty']  = array(
			'current' 		=> $result['folderRoot'],
			'folderList' 	=> array(),
			'fileList' 		=> array(),
			'pathSafe'		=> $type,
			'pathSafeMsg'	=> $result['message'],
			'thisPath'		=> $result['folderRoot']['path'],
		);
		//trace_log($result);
		return $result;
	}
	
	// 私密保险箱根目录; 包含功能入口(未开启-提示开启; 登录)
	public function listRoot(){
		$spaceInfo = $this->spaceInfo();
		if($spaceInfo['type'] != 'isLogin'){ // 未登录访问拦截;
			return show_json($spaceInfo['listEmpty'],true);
		}

		// 进入目录;
		$folder = '{source:'.$spaceInfo['folder'].'}/';
		$listData = Action("explorer.list")->path($folder);
		return show_json($listData);
	}

	// 私密空间功能入口(复用列表接口,不做权限拦截)
	public function action(){
		$spaceInfo = $this->spaceInfo();
		$type = $spaceInfo['type'];
		if($this->in['type'] != 'open' && $type == 'isNotOpen'){
			return show_json($spaceInfo['listEmpty'],true);
		}
		
		$userInfo = Model("User")->getInfoFull(USER_ID);
		$userID   = $userInfo['userID'];
		switch ($this->in['type']){
			case 'open':
				if(Model("SystemOption")->get('pathSafeSpaceEnable') != '1'){ // 未开启私密保险箱功能;
					show_json(LNG('explorer.safe.isNotOpen').' [admin]',false);
				}
				if($type != 'isNotOpen'){show_json(LNG('explorer.safe.doOpenOpend'),false);}
				if(!$userInfo['email']){show_json(LNG('explorer.safe.doOpenTips'),false);}
				
				$password = Input::get('password','require');
				Model("Source")->userPathSafeAdd($userID);
				Model("User")->metaSet($userID,'pathSafePassword',md5($password));
				Action('user.index')->refreshUser($userID);
				show_json(LNG('explorer.safe.doOpenSuccess'),true);
				break;
			case 'login':
				if($type == 'isLogin'){return show_json(LNG('explorer.success'),true);}
				$password = Input::get('password','require');
				if(md5($password) != _get($userInfo,'metaInfo.pathSafePassword')){
					show_json(LNG('ERROR_USER_PASSWORD_ERROR'),false);
				}
				Session::set('pathSafe-userIsLogin','1');
				show_json(LNG('explorer.safe.doLoginOk'),true);
				break;
			case 'logout':
				if($type != 'isLogin'){show_json(LNG('user.loginFirst'),false);}
				Session::remove('pathSafe-userIsLogin');
				show_json(LNG('explorer.success'),true);
				break;
			case 'close': // 关闭私密保险箱; 移动到根目录,清空密码;
				if($type != 'isLogin'){return show_json($spaceInfo['listEmpty'],true);}
				$userRoot   = $userInfo['sourceInfo']['sourceID'];
				$safeFolder = _get($userInfo,'metaInfo.pathSafeFolder','');
				
				Model("Source")->where( array("sourceID"=> $safeFolder) )->data(array('fileType'=>''))->save();
				Model("Source")->move($safeFolder,$userRoot,REPEAT_RENAME_FOLDER,LNG('explorer.safe.name').'-copy');
				Model("Source")->metaSet($safeFolder,'pathSafeFolderUser',null);
				Model("User")->metaSet($userID,'pathSafeFolder',null);
				Model("User")->metaSet($userID,'pathSafePassword',null);
				Session::remove('pathSafe-userIsLogin');
				Action('user.index')->refreshUser($userID);
				show_json(LNG('explorer.success'),true);
				break;
			case 'resetPassword':
				$data = Input::getArray(array(
					'passwordOld'	=> array('check'=>'require'),
					'password'		=> array('check'=>'require'),
				));
				$password = Input::getArray('password','require');
				if(md5($data['passwordOld']) != _get($userInfo,'metaInfo.pathSafePassword')){
					show_json(LNG('user.oldPwdError'),false);
				}
				Model("User")->metaSet($userID,'pathSafePassword',md5($data['password']));
				Session::remove('pathSafe-userIsLogin');
				Action('user.index')->refreshUser($userID);
				show_json(LNG('explorer.safe.passwordChanged'),true);
				break;
			case 'findPasswordSendCode':
				$cacheKey   = 'pathSafe-findPasswordCheckCode_'.$userID;
				$checkInfo  = Cache::get($cacheKey);
				if(is_array($checkInfo) && time() - $checkInfo['time'] < 60){ //一分钟内只发送一次;
					$message = LNG('explorer.safe.sendMailTips').clear_html($userInfo['email'])."<br/>".LNG('explorer.safe.sendMailGet');
					show_json($message."<br/>".LNG('explorer.safe.doCheckLimit'),true);
				}
				$checkInfo  = array('code'=> rand_string(6,1),'time'=>time(),'use'=>0);
				Cache::set($cacheKey,$checkInfo,3600);
				$this->sendCheckCode($checkInfo['code']);
				show_json(LNG('explorer.success'),true);
				break;
			case 'findPasswordReset':
				$cacheKey   = 'pathSafe-findPasswordCheckCode_'.$userID;
				$checkInfo  = Cache::get($cacheKey);
				if(!is_array($checkInfo) || time() - $checkInfo['time'] > 60*60){ // 1小时过期
					Session::remove('pathSafe-findPasswordCheckCode');
					show_json(LNG('user.codeExpired'),false);
				}
				$data = Input::getArray(array(
					'checkCode'	=> array('check'=>'require'),
					'password'	=> array('check'=>'require'),
				));
				if($data['checkCode'] != $checkInfo['code']){
					$checkInfo['use'] += 1;
					if($checkInfo['use'] > 5){
						Cache::remove($cacheKey);
						show_json(LNG('user.codeErrorTooMany'),false);
					}
					Cache::set($cacheKey,$checkInfo);
					show_json(LNG('user.codeError'),false);
				}
				Model("User")->metaSet($userID,'pathSafePassword',md5($data['password']));
				Action('user.index')->refreshUser($userID);
				Cache::remove($cacheKey);
				show_json(LNG('explorer.safe.passwordChanged'),true);
				break;
			default:break;
		}
		return false;
	}
	
	// 找回密码发送验证码;
	private function sendCheckCode($code){
		$userInfo = Session::get("kodUser");
		$title  = ' '.LNG('explorer.safe.sendMailTitle');
		$email  = $userInfo['email'];
		$result = Action("user.bind")->sendEmail($email,'pathSafe-findPassword',$title,$code);
		if(!$result['code']){show_json($result['data'],false);}
		show_json(LNG('explorer.safe.sendMailTips').clear_html($email)."<br/>".LNG('explorer.safe.sendMailGet'),true);
	}
}
listSearch.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listSearch.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/
class explorerListSearch extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	
	// {search}/key=val@key2=value2;
	public function listSearch($pathInfo){
		set_timeout();
		if( !Action('user.authRole')->authCanSearch() ){
			show_json(LNG('explorer.noPermissionAction'),false);
		}
		return $this->listSearchPath($pathInfo);
	}
	public function listSearchPath($pathInfo){
		$param = array();$paramIn=array();$sourceInfo= array();
		$this->parseParam($pathInfo,$param,$paramIn,$sourceInfo);
		if(	isset($param['option']) &&
			in_array('mutil',$param['option']) && 
			is_array($param['wordsMutil']) ){
			$list = $this->searchMutil($param);
		}else{
			$list = $this->searchData($param);
		}
		
		// 搜索内容,强制列表显示;
		if(in_array('content',$param['option']) && !array_key_exists('listTypeSet',$list)){
			$list['listTypeSet'] = 'list';
		}
		$list['searchParam']  = $paramIn;
		$list['searchParent'] = $sourceInfo;
		if($param['parentPath']){
			$list['searchParent'] = IO::info($param['parentPath']);
		}
		$this->searchResultCount($list);
		return $list;
	}

	private function searchMutil($param){
		$param['words'] = '';
		$list = false;
		if(count($param['wordsMutil']) > 100){
			show_json(LNG('common.numberLimit').'(100)',false);
		}
		foreach($param['wordsMutil'] as $word){
			$param['words'] = $word;
			$find = $this->searchData($param);
			foreach ($find['fileList'] as &$item){$item['searchWords'] = $word;};unset($item);
			foreach ($find['folderList'] as &$item){$item['searchWords'] = $word;};unset($item);
			if(!$list) {$list = $find;continue;}
			
			$list['fileList']   = array_merge($list['fileList'],$find['fileList']);
			$list['folderList'] = array_merge($list['folderList'],$find['folderList']);
		}
		// 合并相同的结果;
		$list['fileList']   = array_values(array_to_keyvalue($list['fileList'],'path'));
		$list['folderList'] = array_values(array_to_keyvalue($list['folderList'],'path'));
		
		$list['pageInfo']['totalNum']  = count($list['fileList']) + count($list['folderList']);
		$list['pageInfo']['pageTotal'] = 1;
		$list['disableSort'] = 1;
		return $list;
	}
	
	public function searchData($param){
		$path  = $param['parentPath'];
		$parse = KodIO::parse($path);$parseBefore = $parse;
		$io = IO::init($path);
		if($io->pathParse['truePath']){ // [内部协作分享+外链分享]-物理路径及io路径;
			$path  = $io->pathParse['truePath'];
			$parse = KodIO::parse($path);
		}
		if($parse['type'] == KodIO::KOD_SHARE_LINK && !$io->pathParse['truePath']){ // 外链分享-物理路径及io路径;
			$param['parentID'] = $io->path;
		}
		$result = Hook::trigger('explorer.listSearch.searchDataBefore',$param);//调整$param;
		if(is_array($result)){$param = $result;}

		//本地路径搜索;
		$searchContent = $param['words'] && in_array('content',$param['option']);
		$listData = array('fileList' => array(),'folderList'=>array());
		$fromIO   = $path && ( in_array($parse['type'],array('',false,KodIO::KOD_IO)) || $searchContent);
		if(is_array($param['fileID'])){$fromIO =  false;}
		if($fromIO){
			unset($param['parentID']);
			$listData = $this->searchIO($path,$param);
		}else{
			if(!$param['parentID']) return $listData;
			$listData = $this->model->listSearch($param);
		}

		$result   = Hook::trigger('explorer.listSearch.searchDataAfter',$param,$listData); // 调整结果
		if(is_array($result)){$listData = $result;}
		// pr($fromIO,$path,$param,$parse,$listData);exit;
		
		$listData = $this->listDataParseShareItem($listData,$parseBefore);
		$listData = $this->listDataParseShareLink($listData,$parseBefore);
		return $listData;
	}
	
	// 内部协作分享搜索
	public function listDataParseShareItem($listData,$parseSearch){
		if($parseSearch['type'] != KodIO::KOD_SHARE_ITEM) return $listData;
		$userShare = Action('explorer.userShare');
		$shareInfo = Model("Share")->getInfo($parseSearch['id']);
		$pathPre = '{shareItem:';
		foreach ($listData as $key => $keyList) {
			if($key != 'folderList' && $key != 'fileList' ) continue;
			$keyListNew = array();
			foreach ($keyList as $source){
				$pathStart = substr($source['path'],0,strlen($pathPre));// 已经处理过
				if($pathStart == $pathPre){$keyListNew[] = $source;continue;}
				
				$source = $userShare->_shareItemeParse($source,$shareInfo);
				if($source){$keyListNew[] = $source;}
			};
			$listData[$key] = $keyListNew;
		}
		return $listData;
	}
	// 外链分享搜索
	public function listDataParseShareLink($listData,$parseSearch){
		if($parseSearch['type'] != KodIO::KOD_SHARE_LINK) return $listData;
		$pathPre = '{shareItemLink:';
		foreach ($listData as $key => $keyList) {
			if($key != 'folderList' && $key != 'fileList' ) continue;
			$keyListNew = array();
			foreach ($keyList as $source){
				$pathStart = substr($source['path'],0,strlen($pathPre));// 已经处理过
				if($pathStart == $pathPre){$keyListNew[] = $source;continue;}

				$source = Action('explorer.share')->shareItemInfo($source);
				if($source){$keyListNew[] = $source;}
			};
			$listData[$key] = $keyListNew;
		}
		return $listData;
	}
	
	private function parseParam($pathInfo,&$param,&$paramIn,&$sourceInfo){
		$paramCheck = array(
			'parentPath'=> 'require',
			'words'		=> 'require',
			'option' 	=> 'require',
			'wordsMutil'=> 'require',
			"sizeFrom"	=> 'float',
			"sizeTo"	=> 'float',
			"timeFrom"	=> 'date',
			"timeTo"	=> 'date',
			'fileType'	=> 'require',//folder|ext;
			"user"		=> 'require',
		);
		$pathInfo['param'] = trim($pathInfo['param'],'/');
		$paramIn = $this->parseSearch($pathInfo['param']);
		$param   = array();
		foreach ($paramCheck as $key => $checkType) {
			if( !isset($paramIn[$key]) ) continue;
			if( !Input::check($paramIn[$key],$checkType) ) continue;
			$param[$key] = $paramIn[$key];
			if($checkType == 'date'){
				$param[$key] = strtotime($paramIn[$key]);
			}
			//文件处理
			if($checkType == 'fileType' && $paramIn[$key] != 'folder'){
				$param[$key] = explode(',',$paramIn[$key]);
			}
			if($key == 'option'){
				$param[$key] = array_filter(explode(',',$paramIn[$key]));
			}
			if($key == 'wordsMutil'){
				$param[$key] = str_replace(array("\r","\r\n"), "\n",$paramIn[$key]);
				$param[$key] = array_filter(explode("\n",$param[$key]));
				$param[$key] = array_unique(array_values($param[$key]));
			}
		}
		
		if(isset($param['sizeFrom'])) $param['sizeFrom'] = intval($param['sizeFrom']);
		if(isset($param['sizeTo'])) $param['sizeTo'] = intval($param['sizeTo']);
		if(isset($param['timeFrom'])) $param['timeFrom'] = intval($param['timeFrom']);
		if(isset($param['timeTo'])) $param['timeTo'] = intval($param['timeTo']);
		if(!is_array($param['option'])){$param['option'] = array();}
		
		$searchPath     	= $param['parentPath'] ? $param['parentPath'] : MY_HOME;
		$param['words'] 	= trim($param['words'], '/');
		$param['parentID']  = $this->searchPathSource($searchPath, $param['option']);
	}
	
	public function searchPathSource($path, $option = false){
		if(!$path) return false;
		$path  = trim($path,'/');
		$parse = KodIO::parse($path);
		// 权限检测; 搜索内容->view;搜索名称->show
		$action = 'view';
		if (is_array($option)) {
			$action = in_array('content', $option) ? 'view' : 'show';
		}
		Action('explorer.auth')->can($path, $action);	// canView
		if($parse['type'] == KodIO::KOD_SHARE_ITEM){
			$shareID  	= $parse['id'];
			$sourceID 	= trim($parse['param'],'/');
			$sourceInfo = Action("explorer.userShare")->sharePathInfo($shareID,$sourceID);
			if(!$sourceInfo){
				show_json(LNG('explorer.noPermissionAction'),false);
			}
		}else if($parse['type'] == KodIO::KOD_SOURCE){
			$sourceInfo = IO::info($path);
		}
		return $sourceInfo ? $sourceInfo['sourceID'] : false;
	}
	
	/**
	 * 本地路径,IO路径搜索;
	 */
	public function searchIO($path,$param){
		$list = IO::listAll($path);
		$fileType = isset($param['fileType']) ? $param['fileType']:'';// folder|allFile|''|ext1,ext2
		$onlyFolder = $fileType == 'folder';  // 仅文件夹
		$onlyFile   = $fileType == 'allFile'; // 仅文件
		$allowExt   = false;
		$searchContent = $param['words'] && in_array('content',$param['option']);
		if($fileType && $fileType != 'folder' && $fileType != 'allFile'){
			$allowExt   = explode(',',strtolower($fileType));
			$onlyFile 	= true;
		}

		$isLocalFile = file_exists($path);
		$result = array('folderList'=> array(),'fileList'=> array());
		$matchMax = 20000; $findNum = 0;
		foreach($list as $item){
			check_abort_echo();
			$isFolder = $item['folder'];
			if($onlyFolder && !$isFolder) continue;
			if($onlyFile && $isFolder) continue;
			if($searchContent && $isFolder) continue;
		
			$name = get_path_this($item['path']);
			$ext  = get_path_ext($name);
			if(!$isFolder && $allowExt && !in_array($ext,$allowExt)) continue;
			
			//搜索文件名;
			if (isset($param['words']) && $param['words'] &&
				!in_array('content',$param['option']) &&
				!$this->matchWords($name,$param['words']) ){
				continue;
			}
			
			if(isset($item['sourceInfo'])){ //IO文件;
				$info = $item['sourceInfo'];
			}else{
				$item['type'] = $item['folder'] ? 'folder':'file';
				$item['name'] = $name;$item['ext'] = $ext;$info = $item;
				// $info = IO::info($item['path']); // 性能优化,不直接获取;
			}
			if(!$info) continue;
			if( $isFolder && ($param['sizeFrom'] || $param['sizeTo'])  ) continue;
			$theTime = isset($info['modifyTime']) ? intval($info['modifyTime']):0;//modifyTime createTime;
			if( $theTime && isset($param['timeFrom']) &&
				$theTime < $param['timeFrom'] ){
				continue;
			}
			if( $theTime && isset($param['timeTo']) &&
				$theTime > $param['timeTo'] ){
				continue;
			}
			if( isset($param['sizeFrom']) && !$isFolder && 
				intval($info['size']) < $param['sizeFrom'] ){
				continue;
			}
			if( isset($param['sizeTo']) && !$isFolder && 
				intval($info['size']) > $param['sizeTo'] ){
				continue;
			}
			if( isset($param['user']) && 
				( (is_array($info['modifyUser']) && $param['user'] != $info['modifyUser']['userID']) ||
				  (is_array($info['createUser']) && $param['user'] != $info['createUser']['userID']) ) ){
				continue;
			}
						
			if (isset($param['words']) && $param['words'] &&
				in_array('content',$param['option']) ){
				if(!$this->searchFile($info,$param['words'],$isLocalFile)){
					continue; // 内容匹配; 
				}
			}
			$findNum ++;
			if($findNum > $matchMax) break;
			
			if(kodIO::pathDriverLocal($info['path'])){
				$infoFile = IO::info($item['path']); // 物理路径获取权限等情况;
				$infoFile = is_array($infoFile) ? $infoFile:array();
				$info = array_merge($infoFile,$info);
			}
			$typeKey = $isFolder ? 'folderList':'fileList';
			$result[$typeKey][] = $info;
		}
		// pr($path,$param,$list,$result,$allowExt);exit;
		return $result;
	}
	
	private function searchFile(&$file,$search,$isLocalFile){
		if($file['size'] <= 3) return false;
		if(is_text_file($file['ext']) && $isLocalFile ){
			if($file['size'] >= 1024*1024*10) return false;
			$content = IO::getContent($file['path']);
		}else{
			$content = Hook::trigger('explorer.listSearch.fileContentText',$file);
			if(!$content && is_text_file($file['ext'])){
				if($file['size'] >= 1024*1024*10) return false;
				$content = IO::getContent($file['path']);
			}
		}
		if(!$content) return false;
		if(!is_text_file($file['ext'])){//单行数据展示
			$find = $this->contentSearchMatch($content,$search);
			if($find){$file['searchContentMatch'] = $find;}
			return $find ? true : false;
		}
		$find = content_search($content,$search,false,505);
		if($find){$file['searchTextFile'] = $find;}
		return $find ? true : false;
	}
	
	// $content中匹配到$word的内容截取; 最大长度默认300; 前一行+
	public function contentSearchMatch(&$content,$word,$maxContent = 300){
		if(!$content || !$word) return false;
		$pose  = stripos($content,$word);
		if($pose === false) return false;
		$start = intval($pose) <= 0 ? 0 : $pose;
		$stopChar = array("\n",' ',',','.','!','.','!',';');
		for($i = $start; $i >= 0; $i--){
			$start = $i;
			if($pose - $i > $maxContent * 0.2 - strlen($word)){break;}
			if(in_array($content[$i],$stopChar)) break;
		}
		$start  = $start <= 20 ? 0 : $start;
		$length = strlen($word) + $maxContent;
		$str 	= utf8Repair(substr($content,$start,$length));
		if(strlen($content) > $start + $length + 10){$str .= '...';}
		$str = str_replace(array("\n\n",'&nbsp;','&quot;'),array("\n",' ','"'),$str);
		
		// pr($word,$pose,$start,$length,$str,$content);exit;
		return $str;
	}
	
	// 多个搜索词并列搜索; "且"语法,返回同时包含的内容;
	private function matchWords($name,$search){
		$wordsArr  = explode(' ',trim($search));
		$result    = true;
		if( strlen($search) > 2 &&
			(substr($search,0,1) == '"' && substr($search,-1)  == '"') ||
			(substr($search,0,1) == "'" && substr($search,-1)  == "'")
		){
			$search = substr($search,1,-1);
			$wordsArr = array($search);
		}
		
		foreach($wordsArr as $searchWord){
			$searchWord = trim($searchWord);
			if(!$searchWord) continue;
			if(stripos($name,$searchWord) === false){
				$result = false;
				break;
			}
		}
		return $result;
	}
	
	private function searchResultCount(&$list){
		if(!$list['searchParam']['words']) return;
		$list['searchCount'] = 0;
		foreach ($list['fileList'] as $item) {
			if(!is_array($item['searchTextFile'])) continue;
			$list['searchCount'] += count($item['searchTextFile']);
		}
	}
	
	public function parseSearch($param){
		if(!$param) return array();
		$param  = ltrim($param,'/');
		$all    = explode('@',$param);
		$result = array();
		foreach ($all as $item) {
			if(!$item || !strstr($item,'=')) continue;
			$keyv = explode('=',$item);
			if(count($keyv) != 2 || !$keyv[0] || !$keyv[1]) continue;
			$value = trim(rawurldecode($keyv[1]));
			if(strlen($value) > 0 ){
				$result[$keyv[0]] = $value;
			}
		}
		return $result;
	}
}
listView.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/listView.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 文件夹首选项(显示模式,排序方式)
 * 跟随文件夹,前端可切换,切换后保持到当前路径配置;并且设置为全局默认值; 保留最近200条记录;
 * 优先级: 指定自己 > 指定最近上级 > 默认
 * -----------------------------------
 * listType 		// list|icon|split 指定显示模式;
 * listSortField 	// name|type|size|modifyTime 指定排序字段;
 * listSortOrder 	// up|down 指定排序方式
 * listIconSize 	// number listType为icon图标模式时图标大小;
 * 
 * 其他文件夹列表接口字段扩展;
 * -----------------------------------
 * listTypeSet 		// list|icon|split 强制指定显示模式; 指定后前端不再能切换显示模式;
 * pageSizeArray 	// [100,200,500,1000,2000,5000] 后端指定分页方式;
 * disableSort		// 0|1 禁用排序;
 * listTypePhoto 	// 0|1 强制相册模式展示;
 */
class explorerListView extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}

	// 数据处理;
	public function listDataSet(&$data){
		if(!$data || !is_array($data['current'])) return;
		if(isset($data['listTypeSet'])) return;
		
		$listTypeAllow = Model("UserOption")->get('listTypeKeep') != '0';
		$listSortAllow = Model("UserOption")->get('listSortKeep') != '0';
		if(!$listTypeAllow && !$listSortAllow) return;

		$findPath = $this->makePathParents($data['current']);
		if($listTypeAllow){$this->listDataSetListType($findPath,$data);}
		if($listSortAllow){$this->listDataSetListSort($findPath,$data);}
	}
	
	private function listDataSetListType($findPath,&$data){
		$dataListType 	= $this->dataGetListType();
		$listType 		= '';$listIconSize = '';
		foreach($findPath as $thePath){
			if(!isset($dataListType[$thePath])) continue;
			$listType = $dataListType[$thePath];break;
		}
		$listTypeInfo = explode(':',$listType);
		$listType 	  = $listTypeInfo[0];
		if(count($listTypeInfo) == 2 && $listTypeInfo[0] == 'icon'){
			$listIconSize = $listTypeInfo[1];
		}
		if($listType){$data['listType'] = $listType;}
		if($listIconSize){$data['listIconSize'] = $listIconSize;}
	}
	private function listDataSetListSort($findPath,&$data){
		$dataListSort = $this->dataGetSort();
		$sortBy = '';$sortField = '';$sortOrder = '';
		foreach($findPath as $thePath){
			if(!isset($dataListSort[$thePath])) continue;
			$sortBy = $dataListSort[$thePath];break;
		}
		$sortByInfo   = explode(':',$sortBy);
		if(count($sortByInfo) == 2){
			$sortField = $sortByInfo[0];
			$sortOrder = $sortByInfo[1];
		}
		if($sortField){$data['listSortField'] = $sortField;}
		if($sortOrder){$data['listSortOrder'] = $sortOrder;}
	}
	
	// 获取目录所有上级目录;
	private function makePathParents($pathInfo){
		$parents = array();
		if(_get($pathInfo,'sourceID')){
			if(!_get($pathInfo,'parentLevel')){
				$sourceInfo = Model('Source')->sourceInfo($pathInfo['sourceID']);
				$pathInfo['parentLevel'] = $sourceInfo ? $sourceInfo['parentLevel'] : '';
			}
			$parents   = Model("Source")->parentLevelArray($pathInfo['parentLevel']);
			$parents[] = $pathInfo['sourceID'];
		}else{
			$last = '';
			$pathArr = explode('/', rtrim($pathInfo['path'],'/'));
			for ($i = 0; $i < count($pathArr); $i++){
				$last = $last.$pathArr[$i].'/';
				$parents[] = $last;
			}
		}
		return array_reverse($parents);
	}

	private function dataGetListType(){
		$data =  Model("UserOption")->get('listType','folderInfo',true);
		return is_array($data) ? $data : array();
	}
	private function dataGetSort(){
		$data =  Model("UserOption")->get('listSort','folderInfo',true);
		return is_array($data) ? $data : array();
	}
	public function dataSave($param){
		$listType 	= Input::get('listViewKey','in',null,array('listType','listSort'));
		$value 		= Input::get('listViewValue','require');
		$path 		= Input::get('listViewPath','require');

		// 清空数据;
		if(isset($param['clearListView']) && $param['clearListView'] == '1'){
			Model("UserOption")->remove('listType','folderInfo');
			Model("UserOption")->remove('listSort','folderInfo');
			return;
		}

		$listTypeAllow = Model("UserOption")->get('listTypeKeep') != '0';
		$listSortAllow = Model("UserOption")->get('listSortKeep') != '0';
		if(!$listTypeAllow && $listType == 'listType') return;
		if(!$listSortAllow && $listType == 'listSort') return;
		
		$numberMax 	= 200;
		$listData 	= Model("UserOption")->get($listType,'folderInfo',true);
		$storePath 	= $this->parseStorePath($path).'';
		if(!is_array($listData)){$listData = array();}
		if(isset($listData[$storePath])){unset($listData[$storePath]);}
		if(count($listData) >= $numberMax){
			$listData = array_slice($listData,count($listData) - $numberMax + 1,$numberMax - 1,true);
		}
		// trace_log(array($path,$storePath,$value));
		$listData[$storePath] = $value;
		Model("UserOption")->set($listType,$listData,'folderInfo');
	}
	
	private function parseStorePath($path){
		$pathParse = KodIO::parse($path);
		$thePath   = rtrim($path,'/').'/';
		if($pathParse['type'] == '') return $thePath;
		if($pathParse['type'] == KodIO::KOD_SOURCE) return $pathParse['id'];
		if($pathParse['type'] == KodIO::KOD_SHARE_ITEM){
			$shareInfo = Model('Share')->getInfo($pathParse['id']);
			if($shareInfo && $shareInfo['sourceID'] != '0') return trim($pathParse['param'],'/');
		}
		return $thePath;
	}
}
publish.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/publish.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

// 文件发布: 首先复制到系统临时目录,并开放该目录给指定用户可见; 发布:从临时目录复制到发布目录;

class explorerPublish extends Controller{
	function __construct(){
		parent::__construct();
	}
	
	// 构造发布临时目录并生成访问路径;
	public function makeTemp($fromPath,$publishPath,$userAuth){
		$fromPathInfo = IO::info($fromPath);
		$destPathInfo = IO::info($publishPath);
		if(!$fromPathInfo || !$destPathInfo || $destPathInfo['type'] != 'folder' ) return;
		
		$systemPublish = KodIO::systemPath('systemPublish');
		$tempPath = IO::copy($fromPath,$systemPublish);
		$tempPathInfo = IO::info($tempPath);
		if(!$tempPathInfo) return;
		
		$publishData = array(
			'fromPath' 		=> $fromPath,
			'publishPath'	=> $publishPath,
			'time'			=> time(),
		);
		$publishData = json_encode($publishData);
		Model('Source')->metaSet($tempPathInfo['sourceID'],'publishData',$publishData);
		
		$viewPath = $this->makeShare($tempPath,$userAuth);		
		return array('tempPath'=>$tempPath,'viewPath'=>$viewPath);
	}
	
	// 取消临时文件(夹)
	public function cancle($tempPath){
		$pathInfo = IO::info($tempPath);
		if(!$tempPath || !$pathInfo || !$pathInfo['sourceID']) return;
		$this->removeShare($tempPath);
		IO::remove($tempPath);
	}
	
	// 发布
	public function finished($tempPath){
		$pathInfo = IO::info($tempPath);
		$publishData = _get($pathInfo,'metaInfo.publishData');
		if(!$tempPath || !$publishData) return;
		$publishData = json_decode($publishData,true);
		if(!$publishData || !IO::info($publishData['publishPath'])) return false;
		
		IO::copy($tempPath,$publishData['publishPath']);
		$this->cancle($tempPath);
	}
	
	
	/**
	 * 构建临时访问路径;
	 * authTo: [{"targetType":"1","targetID":"23","authID":"1"},...]
	 */
	private function makeShare($tempPath,$userAuth){
		$pathInfo = IO::info($tempPath);
		$authTo   = array();
		foreach ($userAuth as $userID=>$authID){
			if(!$userID || !$authID) continue;
			$authTo[] = array("targetType"=>SourceModel::TYPE_USER, 'targetID'=>$userID,"authID"=>$authID);
		}
		if(!$authTo || !$pathInfo) return false;
		
		$data = array('isShareTo' => 1,'authTo'=> $authTo);
		$shareID = Model('Share')->shareAdd($pathInfo['sourceID'],$data);
		return KodIO::makePath(KodIO::KOD_SHARE_ITEM,$shareID,$pathInfo['sourceID']);
	}
	private function removeShare($tempPath){
		$pathInfo = IO::info($tempPath);
		$shareID  = $pathInfo['sourceInfo']['shareInfo']['shareID'];
		if(!$shareID) return;
		Model('Share')->remove(array($shareID));
	}
}
recycleDriver.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/recycleDriver.class.php'
View Content
<?php


/**
 * 物理文件夹删除;
 * 回收站支持处理;
 */
class explorerRecycleDriver extends Controller{
	public function __construct(){
		parent::__construct();
	}
	public function removeCheck($path,$toRecycle=true){
		$path 		= KodIO::clear($path);
		$pathParse  = KodIO::parse($path);
		$driver 	= IO::init($path);
		if(!$toRecycle || $pathParse['type'] == KodIO::KOD_SOURCE){
			return IO::remove($path,$toRecycle);
		}
		$recycleLocal = rtrim(DATA_PATH,'/').'/'.$this->localRecycleFolder().'/';
		$recyclePath  = rtrim($pathParse['pathBase'],'/').'/.recycle/';
		if(!$pathParse['type']){$recyclePath = $recycleLocal;}
		
		// 协作分享内容: 某人删除时, 移动到自己的回收站(物理路径及io路径; db路径处理)
		if($pathParse['type'] == KodIO::KOD_SHARE_ITEM && $driver->getType() == 'dbshareitem'){
			$recyclePath  = rtrim($driver->getPathRoot(),'/').'/.recycle/';
		}
		return $this->moveToRecycle($path,$recyclePath,IO::pathFather($path));
	}
	
	/**
	 * 追加物理路径
	 */
	public function appendList(&$data,$pathParse){
		if($pathParse['type'] != KodIO::KOD_USER_RECYCLE) return;
		$list = $this->listData();
		$listNew = $list;
		foreach ($list as $toPath => $fromPath){
			$parse = KodIO::parse($toPath);
			if($parse['driverType'] == 'io') {
				$info = Model('Storage')->driverInfo($parse['id']);
				if(!$info) {
					unset($listNew[$toPath]);
					continue;
				}
			}
			$ioType = IO::getType($toPath);
			$allowInfo = $ioType == 'local' || $ioType == 'dbshareitem'; // 仅允许本地存储属性访问; 访问速度优化处理;
			if(!$allowInfo){
				$name = get_path_this($toPath);
				$info = array(
					'path' => rtrim($fromPath,'/').'/'.$name,
					'name' => $name,
					'type' => preg_match("/.+\.[a-zA-Z0-9]{1,6}$/",$name,$match) ? 'file' : 'folder',
					'size' => '-1',
				);
				if($info['type'] == 'folder'){
					$data['folderList'][] = $info;
				}else{
					$data['fileList'][] = $info;
				}
				continue;
			}
			
			// $pathParse['type'];local;miniIO;  info; 网络存储则不获取属性;			
			$info = IO::info($toPath);
			if(!$info){
				unset($listNew[$toPath]);
				continue;
			}
			
			// 协作分享,删除到回收站的内容; 有权限时数据会重复/去重处理;
			if($info['sourceID']){
				foreach($data['fileList'] as $i=>$item) {
					if($info['sourceID'] == $item['sourceID']){unset($data['fileList'][$i]);}
				}
				foreach($data['folderList'] as $index=>$item) {
					if($info['sourceID'] == $item['sourceID']){unset($data['folderList'][$i]);}
				}
			}
			
			$info['path'] = rtrim($fromPath,'/').'/'.$info['name'];
			if($parse['type'] == KodIO::KOD_SHARE_ITEM){$info['path'] = $toPath;}
			if($info['type'] == 'folder'){
				$data['folderList'][] = $info;
			}else{
				$data['fileList'][] = $info;
			}
		}
		
		// 有不存在内容则自动清除;
		if(count($listNew) != count($list)){
			$this->resetList($listNew);
		}
		$data['folderList'] = array_values($data['folderList']);
		$data['fileList']   = array_values($data['fileList']);
		// pr($data);exit;
	}
	
	// 彻底删除;
	public function remove($sourceArr){
		$list = $this->listData();
		$listNew = $list;
		foreach ($list as $toPath => $fromPath){
			// 删除所有, 或者当前在待删除列表中则删除该项;
			$beforePath = rtrim($fromPath,'/').'/'.get_path_this($toPath);
			if(!$sourceArr || 
				in_array($beforePath,$sourceArr) || 
				in_array(trim($beforePath,'/').'/',$sourceArr)
			){
				IO::remove($toPath,false);
				unset($listNew[$toPath]);
			}
		}
		if(count($listNew) != count($list)){
			$this->resetList($listNew);
		}
	}

	// 还原
	public function restore($sourceArr){
		$list = $this->listData();
		$listNew = $list;
		foreach($list as $thePath => $beforePath){
			IO::move($thePath,$beforePath,REPEAT_RENAME_FOLDER);
			unset($listNew[$thePath]);
		}
		if(count($listNew) != count($list)){
			$this->resetList($listNew);
		}
	}
	
	/**
	 * 删除到回收站;
	 * 物理路径: 移动到 TEMP_PATH/.recycle/[user_id]
	 * io路径  : 移动到该io/.recycle 下;
	 */
	private function moveToRecycle($path,$recyclePath,$beforePath){
		if(substr($path,0,strlen($recyclePath)) == $recyclePath){
			return IO::remove($path,false);//已经在回收站中,则不再处理;
		}
		
		$destPath = IO::mkdir($recyclePath);
		$destInfo = $destPath ? IO::info($destPath):false;
		if(!$destInfo){return $path;} //新建失败则返回;
		if(IO::isParentOf($destPath,$path)){return IO::remove($path,false);} // 已经在回收站中则直接删除;

		$toPath = IO::move($path,$destPath,REPEAT_RENAME_FOLDER);
		$list = $this->listData();
		$list[$toPath] = $beforePath;
		$this->resetList($list);
		return $toPath;
	}
	private function listData(){
		$this->localRecycleFolder();
		$list = Model("UserOption")->get('recycleList','recycle');
		return $list ? json_decode($list,true):array();
	}
	private function resetList($list){
		$listData = json_encode($list);
		// options表key=>value value最长长度限制;
		if(strlen($listData) > 65536){
			show_json(LNG('explorer.recycleClearForce'),false);
		}
		Model("UserOption")->set('recycleList',$listData,'recycle');
	}
	
	// 物理路径回收站文件名处理(安全优化, 隐藏文件夹名,避免被遍历)
	private function localRecycleFolder(){
		// Model("SystemOption")->set('recycleFolder','');return '.recycle';//debug;
		$recycleFolder = Model("SystemOption")->get('recycleFolder');
		if($recycleFolder) return $recycleFolder;
		
		$recycleFolder = '.recycle_'.rand_string(8);
		$recycleLocal  = DATA_PATH.$recycleFolder.'/';
		
		// =====1.35前版本兼容处理;====
		$recycleBefore= DATA_PATH.'.recycle/';
		$list = Model("UserOption")->get('recycleList','recycle');
		$list = $list ? json_decode($list,true):array();
		$listNew = array();
		foreach ($list as $path => $beforeAt){
			$newPath = $path;
			if(substr($path,0,strlen($recycleBefore)) == $recycleBefore){
				$newPath = $recycleLocal.substr($path,strlen($recycleBefore));
			}
			$listNew[$newPath] = $beforeAt;
		}
		Model("UserOption")->set('recycleList',json_encode($listNew),'recycle');
		@rename($recycleBefore,get_path_father($recycleBefore).'/'.$recycleFolder);
		// =====兼容end====

		IO::mkdir($recycleLocal);
		file_put_contents($recycleLocal.'index.html','');
		Model("SystemOption")->set('recycleFolder',$recycleFolder);
		return $recycleFolder;
	}
}
seo.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/seo.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 搜索引擎优化;
 */
class explorerSeo extends Controller{
	function __construct(){
		parent::__construct();
	}
	public function check(){
		I18n::set('title','');
		Hook::bind('templateCommonFooter','explorer.seo.makeFooter');
		if(MOD == 'sitemap'){$this->siteMap();}
		if(MOD == 's'){$this->shareView(ST);}
	}
	public function makeFooter(){
		$powerBy 	= LNG("common.copyright.powerBy");
		$homePage	= str_replace('%s',APP_HOST,LNG('common.copyright.homepage'));

		$isKeep   = _get($this->in,'keep') == '1' ? 'keep=1':null;
		$siteMap  = urlApi('sitemap',$isKeep);		
		$link  = 'https://github.com/kalcaddle/kodbox';
		$html  = "<div class='page-footer' style='display:none;'>\n\t";
		$html .= "<h3>{$powerBy} <a href='{$link}' target='_blank'>V".KOD_VERSION."</a></h3>\n\t";
		$html .= "<h3>{$homePage}</h3>\n\t";
		$html .= "<h3><a href='".$siteMap."'>Files</a></h3></div>\n\t";
		echo $html;
	}
	
	/**
	 * 搜索引擎抓取:
	 * 1. 外链分享列表;分页处理;
	 * 2. 外链分享文件概览,文件夹子文件 (文本文件内容截取10k);
	 * 3. 外链分享文件夹列表;
	 */
	public function siteMap(){
		if($this->config['settings']['allowSEO'] != 1){ // keep=1
			header('HTTP/1.1 404 Not Found');
			return show_tips("Not allow robots!");
		}
		if(!defined("USER_ID")){define("USER_ID",0);}
		switch(ST){
			case 'index': $this->shareList();exit;break;
			case 'share': $this->shareView(ACT);exit;break;
			case 'file' : $this->shareFileOut();exit;break;
			default: break;
		}
	}

	private function displayContent($html,$link=''){
		if(!$html) return;
		$link = $link ? $link : APP_HOST;
		$location = '<script type="text/javascript">window.location.href="'.$link.'"</script>';
		$GLOBALS['templateContent'] = "<div class='page-view-search'>{$html}</div>";
		if(_get($this->in,'keep') != '1'){
			// pr_trace($link);exit;
			$GLOBALS['templateContent'] =$location.$GLOBALS['templateContent'];
		}
		Hook::bind('templateCommonContent','explorerSeo.echoContent');
		include(TEMPLATE.'user/index.html');
	}
	private function displayError($content,$link=''){
		header('HTTP/1.1 404 Not Found');
		$html = '<div class="info-alert info-alert-red"><p>'.$content.'</p></div>';
		$this->displayContent($html,$link);
	}
	public function echoContent(){
		echo "<style>
			body{
				position:relative !important;width:auto;height:auto;
				background:#f0f2f5;overflow:auto !important;
			}
			.page-footer{display:block !important;text-align: center;color:#aaa;}
			.page-footer h3{display:inline-block;font-size: 13px;font-weight:400;}
		</style>";
		echo $GLOBALS['templateContent'];
	}
	
	// 分享列表;
	private function shareList(){
		$title = LNG('explorer.share.linkTo');
		I18n::set('title',$title.' - ');
		$model = Model('Share');
		$where = array(
			'isLink'	=> '1',
			'password'	=> '',
			'timeTo'	=> array('<',time())
		);
		$this->in['pageNum'] = 15;//>5;
		$this->in['page'] 	 = _get($this->in,'page','1');
		$list = $model->where($where)->order("createTime desc")->selectPage();

		$listHtml = '';
		$isKeep   = _get($this->in,'keep') == '1' ? 'keep=1':null;
		$siteMap  = urlApi('sitemap/',$isKeep);
		$pageHtml = $this->makePage($list['pageInfo'],$siteMap,5);
		$list = Model('Share')->listDataApply($list['list']);
		foreach ($list as $item){
			$listHtml .= $this->shareMakeItem($item);
		}
		if(!$list){
			$listHtml = "<div class='grey-6 align-center mt-30'>".LNG('common.empty')."</div>";
		}else{
			$listHtml = "
			<li class='file-item header'>
				<span class='title-item item-name'>".LNG('common.name')."</span>
				<span class='title-item item-user'>".LNG('explorer.auth.share')."</span>
				<span class='title-item item-size'>".LNG('explorer.file.size')."</span>
				<span class='title-item item-time'>".LNG('explorer.file.shareTime')."</span>
			</li>".$listHtml;
		}
		$html = "<h3>{$title}</h3><ul class='list-file list-file-share'>$listHtml</ul>$pageHtml";
		$this->displayContent($html);
	}
	
	/**
	 * 分享内容: 分享文件;分享文件夹;分享文件夹子文件
	 * 分享信息: 分享信息,路径信息,文件; 文件夹--列表
	 * 文件展示: 文本-展示1M; 图片展示img; 其他文件:下载链接; (有预览权限)
	 */
	private function shareView($hash){
		$viewPath 	= trim(_get($this->in,'view',''),'/');
		$view  		= $viewPath ? '&view='.rawurlencode($viewPath):'';
		$linkTrue 	= APP_HOST.'#s/'.$hash.str_replace('%2F','/',$view);
		
		$shareInfo 	= Model('Share')->getInfoByHash($hash);
		$error  	= $this->shareCheck($shareInfo);		
		if($error) return $this->displayError($error,$linkTrue);
		
		$sourcePath = $shareInfo['sourcePath'].'/'.$viewPath;
		$shareDesc 	= $this->shareMakeItem($shareInfo);
		$linkPage 	= $this->shareLink($shareInfo);
		$addressHtml = "<a href='{$linkPage}'>{$shareInfo['title']}</a>";
		$viewPathArr = explode('/',$viewPath);
		for($i = 0; $i < count($viewPathArr); $i++){
			if(!$viewPathArr[$i]) continue;
			$viewPathNow = implode('/',array_slice($viewPathArr,0,$i+1));
			$link = $this->shareLink($shareInfo,$viewPathNow);			
			$addressHtml .= " / <a href='{$link}'>".htmlentities($viewPathArr[$i])."</a>";
		}
		$html = "
		<div class='share-header-info'>{$shareDesc}</div>
		<div class='address-info'>".LNG('common.position').": ".$addressHtml."</div>";

		$pathInfo = IO::infoFull($sourcePath);
		if(!$pathInfo) return $this->displayError(LNG('common.pathNotExists'),$linkTrue);
		
		// 标题处理;
		$currentName = $viewPath ? $pathInfo['name'].' - ' : '';
		I18n::set('title',$currentName.$shareInfo['title'].' - ');
		
		if($pathInfo['type'] == 'file'){
			$movieFile = explode(',','mov,mp4,webm,m4v,mkv');
			$imageFile = explode(',','jpg,jpeg,png,bmp,ico,gif,webp');
			$linkFile  = $this->shareLink($shareInfo,$viewPath,'file');
			if(is_text_file($pathInfo['ext'])){
				$content = IO::fileSubstr($pathInfo['path'],0,1024*500);
				$html .= "<p><pre class='the-code'><code>".htmlentities($content)."</code></pre></p>";
				$html .= "<script src='".STATIC_PATH."app/vender/markdown/highlight.min.js'></script>
					<script>hljs.initHighlightingOnLoad();</script>";
			}else if(in_array($pathInfo['ext'],$imageFile)){
				$html .= "<p class='content-file'><img src='{$linkFile}' /></p>";
			}else if(in_array($pathInfo['ext'],$movieFile)){
				$html .= "<video class='content-file' src='{$linkFile}' controls='controls'></video>";
			}else{
				$html .= "
				<div class='content-download'>
					<a class='kui-btn kui-btn-blue' href='{$linkFile}' 
					target='_blank'>".LNG('common.download')."</a>
					<div class='grey-6'>".LNG('explorer.share.errorShowTips')."</div>
				</div>";
			}
		}else{
			$html .= $this->shareViewFolder($shareInfo,$pathInfo);
		}
		$this->displayContent($html,$linkTrue);
	}
	private function shareFileOut(){
		$shareInfo= Model('Share')->getInfoByHash(ACT);
		$error = $this->shareCheck($shareInfo);
		if($error) return $this->displayError($error);

		$viewPath   = trim(_get($this->in,'view',''),'/');
		$sourcePath = $shareInfo['sourcePath'].'/'.$viewPath;
		$pathInfo = IO::infoFullSimple($sourcePath);
		if(!$pathInfo) return $this->displayError(LNG('common.pathNotExists'));
		IO::fileOut($pathInfo['path']);exit;
	}
	
	private function shareViewFolder($shareInfo,$pathInfo){
		$pathPre 	= _get($shareInfo['sourceInfo'],'pathDisplay',$shareInfo['sourceInfo']['path']);
		$list 		= IO::listPath($pathInfo['path']);
		$fileList 	= KodSort::arraySort($list['fileList'],'name');
		$folderList = KodSort::arraySort($list['folderList'],'name');
		$listAll 	= array_merge($folderList,$fileList);
		$listAll 	= array_slice($listAll,0,2000);
				
		$listHtml 	= '';
		foreach ($listAll as $pathInfo){
			$pathDisplay = _get($pathInfo,'pathDisplay',$pathInfo['path']);
			if(substr($pathDisplay,0,strlen($pathPre)) != $pathPre) continue;
			$viewPath = substr($pathDisplay,strlen($pathPre));
			$link = $this->shareLink($shareInfo,$viewPath);
			$time = date('Y-m-d H:i',$pathInfo['createTime']);
			$size = size_format($pathInfo['size']);
			$size = $pathInfo['type'] != 'folder' || $pathInfo['size'] ? $size:'';
			$ext  = $pathInfo['type'] == 'folder' ? 'folder' : $pathInfo['ext'];
			$listHtml .= "
			<li class='file-item {$pathInfo['type']}'>
				<a class='file-link' href='{$link}'></a>
				<span class='title-item item-name'>
					<i class='path-ico'><i class='x-item-icon x-{$ext} small'></i></i>
					<a href='{$link}'>".htmlentities($pathInfo['name'])."</a>
				</span>
				<span class='title-item item-size'>{$size}</span>
				<span class='title-item item-time'>{$time}</span>
			</li>";
		}
		if(!$listAll){
			$listHtml = "<div class='grey-6 align-center mt-30'>".LNG('common.empty')."</div>";
		}else{
			$listHtml = "
			<li class='file-item header'>
				<span class='title-item item-name'>".LNG('common.name')."</span>
				<span class='title-item item-size'>".LNG('explorer.file.size')."</span>
				<span class='title-item item-time'>".LNG('common.createTime')."</span>
			</li>".$listHtml;
		}
		return "\n<ul class='list-file list-file-folder'>".$listHtml."</ul>\n";
	}
	private function shareLink($shareInfo,$viewPath='',$page='share'){
		$view  = $viewPath ? '&view='.rawurlencode($viewPath):'';
		$view  = str_replace('%2F','/',$view);
		$keep  = _get($this->in,'keep') == '1' ? '&keep=1' : '';
		return urlApi('sitemap/'.$page.'/'.$shareInfo['shareHash'],ltrim($keep.$view,'&'));
	}
	
	// 分享权限处理;
	private function shareCheck($shareInfo){
		$msg = array(
			'notExists' 	=> LNG('explorer.share.notExist'),
			'needPassword' 	=> LNG('explorer.share.needPwd'),
			'onlyLogin'		=> LNG('explorer.share.onlyLogin'),
			'timeout' 		=> LNG('explorer.share.errorTime'),
			'notDownload' 	=> LNG('explorer.share.noDownTips'),
			'downloadLimit'	=> LNG('explorer.share.downExceedTips'),
		);
		if(!$shareInfo || !is_array($shareInfo)) return $msg['notExists'];
		
		$downloadNumber = _get($shareInfo,'options.downloadNumber');
		$downloadLimit  =  $downloadNumber && intval($downloadNumber) > intval($shareInfo['numDownload']);	
		if($shareInfo['timeTo'] && intval($shareInfo['timeTo']) < time()) return $msg['timeout'];
		if($shareInfo['password']) return $msg["needPassword"];
		if(_get($shareInfo,'options.notDownload') == '1') return $msg['notDownload'];
		if(_get($shareInfo,'options.onlyLogin') == '1') return $msg['onlyLogin'];
		if($downloadLimit) return $msg['downloadLimit'];
		return false;
	}
	private function shareMakeItem($item){
		if($this->shareCheck($item)) return '';
		$pathInfo = $item['sourceInfo'];
		if(!$item['userInfo']){
			$userList = Model('User')->userListInfo(array($item['userID']));
			$item['userInfo'] = $userList[$item['userID']];
		}
		if(!$pathInfo && $item['sourceID'] == '0'){
			$pathInfo = IO::info($item['sourcePath']);
		}
		if(!$pathInfo) return '';
		$link = $this->shareLink($item);
		$user = $item['nickName'] ? $item['nickName']:$item['name'];

		$time = date('Y-m-d H:i',$item['createTime']);	
		$size = size_format($pathInfo['size']);
		$size = $pathInfo['type'] != 'folder' || $pathInfo['size'] ? $size:'';
		$ext  = $pathInfo['type'] == 'folder' ? 'folder' : $pathInfo['ext'];
		return "
		<li class='file-item'>
			<a class='file-link' href='{$link}'></a>
			<span class='title-item item-name'>
				<i class='path-ico'><i class='x-item-icon x-{$ext} small'></i></i>
				<a href='{$link}'>".htmlentities($item['title'])."</a>
			</span>
			<span class='title-item item-user'>".htmlentities($user)."</span>
			<span class='title-item item-size'>{$size}</span>
			<span class='title-item item-time'>{$time}</span>
		</li>";
	}
	private function makePage($info,$linkPre,$showNum=5){
		if($showNum <= 2 || !$info || $info['pageTotal'] == 0 ) return '';
		$total 	= $info['pageTotal'];
		$pageDesc = 
		"<span class='page-info-text'>
			{$total}".LNG('explorer.table.page')." <span class='grey-6'>
			({$info['totalNum']}".LNG('explorer.table.items').")</span>
		</span>";
		if($total <= 1) return "<div class='page-box'>\n{$pageDesc}</div>";
		
		$from 	= $info['page'] - intval(($showNum - 1) / 2);
		$to   	= $info['page'] + intval(($showNum - 1) / 2) + ($showNum % 2 == 0 ? 1 : 0);
		$from 	= $to   > $total ? ($total - $showNum + 1) : $from;
		$to 	= $from < 1 ? $showNum : $to;
		$from 	= $from <= 1 ? 1 : $from;
		$to 	= $to   >= $total ? $total : $to;
		
		$html 	= '';
		for($i = $from; $i<=$to; $i++){
			if($i == $info['page']){
				$html .= "<a href='{$linkPre}&page={$i}' class='current'>{$i}</a>\n";
			}else{
				$html .= "<a href='{$linkPre}&page={$i}'>{$i}</a>\n";
			}
		}
		if($total > $showNum){
			$html = "<a href='{$linkPre}&page=1' class='page-first'>".LNG('explorer.table.first')."</a>\n".$html;
			$html.= "<a href='{$linkPre}&page={$total}' class='page-last'>".LNG('explorer.table.last')."</a>\n";
		}
		return "<div class='page-box'>\n{$html}{$pageDesc}\n</div>";
	}
}
share.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/share.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerShare extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  = Model('Share');
		$notCheck = array('link','file','pathParse','fileDownloadRemove','unzipListHash','fileGetHash');
		// 检测并处理分享信息
		if( strtolower(ST) == 'share' && !in_array_not_case(ACT,$notCheck) ){
			$shareID = $this->parseShareID();
			$this->initShare($shareID);
			if(strtolower(MOD.'.'.ST) == 'explorer.share'){$this->authCheck();}
		}
	}

	// 自动解析分享id; 通过path或多选时dataArr;
	private function parseShareID(){
		$shareID = $this->in['shareID'];
		if($shareID) return $shareID;
		$thePath = $this->in['path'];
		if(!$thePath && isset($this->in['dataArr'])){
			$fileList = json_decode($this->in['dataArr'],true);
			$thePath  = $fileList[0]['path'];
		}
		$parse = KodIO::parse($thePath);
		if($parse['type'] == KodIO::KOD_SHARE_LINK){
			$shareID = $parse['id'];
		}
		return $shareID;
	}

	// 通用生成外链
	public function link($path,$downFilename=''){
		if(!$path || !$info = IO::info($path)) return;
		$pass = Model('SystemOption')->get('systemPassword');
		$hash = Mcrypt::encode($info['path'],$pass);

		$addParam = '&name=/'.rawurlencode($info['name']);
		if($downFilename){
		    $addParam = '&downFilename='.rawurlencode($downFilename);
		}
		return urlApi('explorer/share/file',"hash={$hash}".$addParam);
	}
	
	// 构造加密链接,相同文件每次一样,确保浏览器能够缓存;
	public function linkFile($file,$addParam=''){
		$pass = Model('SystemOption')->get('systemPassword');
		$hash = Mcrypt::encode($file,$pass,false,'kodcloud');
		return urlApi('explorer/share/file',"hash={$hash}".($addParam ? '&'.$addParam:'') );
	}
	
	public function linkSafe($path,$downFilename=''){
		if(!KodUser::isLogin()){return $this->link($path,$downFilename);}
		if(!$path || !$info = IO::info($path)) return;
		$link = Action('user.index')->apiSignMake('explorer/index/fileOut',array('path'=>$path));
		
		$addParam = '&name=/'.rawurlencode($info['name']);
		$addParam .= $downFilename ? '&downFilename='.rawurlencode($downFilename):'';
		return $link.$addParam;
	}
	
	public function linkOut($path,$token=false){
		$parse  = KodIO::parse($path);
		$info   = IO::info($path);
		$apiKey = 'explorer/index/fileOut';
		if($parse['type'] == KodIO::KOD_SHARE_LINK){
			$apiKey = 'explorer/share/fileOut';
		}
		$etag = substr(md5($path),0,5);
		$name = isset($this->in['name']) ? rawurlencode($this->in['name']):'';
		if($info){
			$name = rawurlencode($info['name']);
			$etag = substr(md5($info['modifyTime'].$info['size']),0,5);
		}
		$url = urlApi($apiKey,"path=".rawurlencode($path).'&et='.$etag.'&name=/'.$name);
		if($token) $url .= '&accessToken='.Action('user.index')->accessToken();
		return $url;
	}
	
	public function file(){
		if(!$this->in['hash']) return;
		$path = $this->fileHash($this->in['hash']);
		$isDownload = isset($this->in['download']) && $this->in['download'] == 1;
		$downFilename = !empty($this->in['downFilename']) ? $this->in['downFilename'] : false;
		Action('explorer.index')->fileOutUpdate($path,$isDownload,$downFilename);
	}
	
	// 文件外链解析;
	private function fileHash($hash){
		if(!$hash || strlen($hash) > 5000) return;
		$path = Mcrypt::decode($hash,Model('SystemOption')->get('systemPassword'));
		$fileInfo = $path ? IO::info($path) : false;
		if(!$fileInfo){show_json(LNG('common.pathNotExists'),false);}
		if($fileInfo['isDelete'] == '1'){show_json(LNG('explorer.pathInRecycle'),false);}
		return $path;
	}
	
	/**
	 * 其他业务通过分享路径获取文档真实路径; 文件打开构造的路径 hash/xxx/xxx; 
	 * 解析路径,检测分享存在,过期时间,下载次数,密码检测;
	 */
	public function sharePathInfo($path,$encode=false,$infoChildren=false){
		$parse = KodIO::parse($path);
		if(!$parse || $parse['type'] != KodIO::KOD_SHARE_LINK){return false;}
		$this->showErrorStop = true;
		$error = $this->initShare($parse['id']);
		$this->showErrorStop = false;
		if($error){
			$GLOBALS['explorer.sharePathInfo.error'] = $error;
			return false; // 不存在,时间过期,下载次数超出,需要登录,需要密码;
		}
		$truePath = $this->parsePath($path);
		if($infoChildren){
			$result = IO::infoWithChildren($truePath);
		}else{
			$result = IO::info($truePath);
		}
		if(!$result){return false;}
		
		// 仅文件,判断预览;
		$canView 	 = _get($this->share,'options.notView') != '1';
		$canDownload = _get($this->share,'options.notDownload') != '1';
		if($this->shareOuterAuth){$canDownload = true;}
		if($result['type'] == 'file' && !$canDownload && !$canView){
			$GLOBALS['explorer.sharePathInfo.error'] = LNG('explorer.share.noViewTips');
			return false;
		}
		if(is_array($result)){
			if($encode){$result = $this->shareItemInfo($result);}
			$result['shareID'] = $this->share['shareID'];
		}
		return $result;
	}
	public function shareInfoLast(){return $this->share;}

	/**
	 * 通过分享hash获取分享信息;
	 * 提交分享密码
	 */
	public function get($get=false){
		$field = array(
			'shareHash','title','isLink','timeTo','numView','numDownload',
			'options','createTime','sourceInfo',
		);
		$data  = array_field_key($this->share,$field);
		$data['shareUser']  = Model("User")->getInfoSimpleOuter($this->share['userID']);
		$data['shareUser']  = $this->filterUserInfo($data['shareUser']);
		$data['sourceInfo'] = $this->shareItemInfo($data['sourceInfo']);
		if($get) return $data;
		show_json($data);
	}
	
	/**
	 * 分享信息初始化;
	 * 拦截相关逻辑
	 * 
	 * 过期拦截
	 * 下载次数限制拦截
	 * 登录用户拦截
	 * 密码检测拦截:如果参数有密码则检测并存储;错误则提示 
	 * 下载次数,预览次数记录
	 */
	public function initShare($hash=''){
		if($this->share && !isset($GLOBALS['isRootCall'])) return;
		$this->share = $share = $this->model->getInfoByHash($hash);
		$this->shareOuterAuth = Action('explorer.shareOut')->sendCheckAuth($this->share);
		if(!$share || $share['isLink'] != '1'){
			return $this->showError(LNG('explorer.share.notExist'),30100);
		}
		if($share['sourceInfo']['isDelete'] == '1'){
			return $this->showError(LNG('explorer.share.notExist'),30100);
		}
		if(isset($GLOBALS['isRootCall']) && $GLOBALS['isRootCall']){return;} //后台任务队列获取属性,不做判断;
		
		//外链分享有效性处理: 当分享者被禁用,没有分享权限,所在文件不再拥有分享权限时自动禁用外链分享;
		if(!Action('explorer.authUser')->canShare($share)){
			$userInfo = Model('User')->getInfoSimpleOuter($share['userID']);
			$tips = '('.$userInfo['name'].' - '.LNG('common.noPermission').')';
			return $this->showError(LNG('explorer.share.notExist') .$tips,30100);
		}

		//检测是否过期
		if($share['timeTo'] && $share['timeTo'] < time()){
			return $this->showError(LNG('explorer.share.expiredTips'),30101,$this->get(true));
		}
		if($this->shareOuterAuth){return;}
		
		//检测下载次数限制
		if( $share['options'] && 
			$share['options']['downloadNumber'] && 
			$share['options']['downloadNumber'] <= $share['numDownload'] ){
			$msg = LNG('explorer.share.downExceedTips');
			$pathInfo = explode('/', $this->in['path']);
			if(!empty($pathInfo[1]) || is_ajax()) {
				return $this->showError($msg,30102,$this->get(true));
			}
			return $this->showError($msg,30102,$this->get(true));
		}
		//检测是否需要登录
		$user = Session::get("kodUser");
		if( $share['options'] && $share['options']['onlyLogin'] == '1' && !is_array($user)){
			return $this->showError(LNG('explorer.share.loginTips'),30103,$this->get(true));
		}
		//检测密码
		$passKey  = 'Share_password_'.$share['shareID'];
		if( $share['password'] ){
			if( isset($this->in['password']) && strlen($this->in['password']) < 500 ){
				$code = md5(BASIC_PATH.Model('SystemOption')->get('systemPassword'));
				$pass = Mcrypt::decode(trim($this->in['password']),md5($code));
				if($pass == $share['password']){
					Session::set($passKey,$pass);
				}else{
					return $this->showError(LNG('explorer.share.errorPwd'),false);
				}
			}
			// 检测密码
			if( Session::get($passKey) != $share['password'] ){
				return $this->showError(LNG('explorer.share.needPwd'),30104,$this->get(true));
			}
		}
	}
	private function showError($msg,$code,$info=false){
		if($this->showErrorStop){return $msg ? $msg:'error';}
		$action = strtolower(ACT);
		$tipsAction = array('fileout','filedownload');
		if(in_array($action,$tipsAction)){return show_tips($msg);}
		show_json($msg,$code,$info);
	}

	/**
	 * 权限检测
	 * 下载次数,预览次数记录
	 */
	private function authCheck(){
		$ACT   		= strtolower(ACT);
		$options 	= is_array($this->share['options']) ? $this->share['options'] : array();
		$where 		= array("shareID"=>$this->share['shareID']);
		// 分享者角色没有上传权限时, 忽略配置开启上传;
		$actionUpload 	= array('fileupload','mkdir','mkfile');
		$actionEdit 	= array('fileupload','mkdir','mkfile','pathrename','pathDelete','pathcopy','pathcute','pathcuteto','pathcopyto','pathpast','filesave');
		$canUpload 		= $options['canUpload']   == '1';
		$canEdit 		= $options['canEditSave'] == '1';
		$canView	 	= $options['notView'] 	  != '1';
		$canDownload 	= $options['notDownload'] != '1';
		$outerAuth 		= $this->shareOuterAuth; // 对外联合分享访问, 权限采用外站分享的权限;
		if($outerAuth == 'read' || $outerAuth == 'write'){$canView = true;$canDownload = true;}
		if($outerAuth == 'write'){$canEdit = true;}
		if($ACT == 'get'){
			$this->model->where($where)->setAdd('numView');
			$this->cacheShareClear();
		}
		
		//权限检测;是否允许下载、预览、上传;
		$isDownload = ($ACT == 'fileout' && $this->in['download']=='1') || $ACT =='zipdownload' || $ACT =='filedownload';
		if(!$canDownload && $isDownload){
			$this->showError(LNG('explorer.share.noDownTips'),false);
		}
		if(!$canView && in_array($ACT,array('fileget','fileout','unziplist'))){
			$this->showError(LNG('explorer.share.noViewTips'),false);
		}
		
		// TODO 有编辑权限,一定包含上传权限;
		if(in_array($ACT,$actionUpload) && !$canEdit){
			if($canUpload && !Action('user.authRole')->authCanUser('explorer.upload',$this->share['userID'])){$canUpload = false;}
			if($canUpload && isset($this->in['path']) && !$this->checkPathAuth($this->in['path'])){$canUpload = false;}
			if(!$canUpload){$this->showError(LNG('explorer.noPermissionWriteAll'),false);}
		}
		if(in_array($ACT,$actionEdit) && !in_array($ACT,$actionUpload)){// 目的地检测;必须为当前分享文件夹下子内容;
			if($canEdit && !Action('user.authRole')->authCanUser('explorer.edit',$this->share['userID'])){$canEdit = false;}
			if($canEdit && isset($this->in['path']) && !$this->checkPathAuth($this->in['path'])){$canEdit = false;}
			if($canEdit && isset($this->in['dataArr']) && !$this->checkPathAuth($this->in['dataArr'],true)){$canEdit = false;}
			if($canEdit && $outerAuth != 'write' && Model('SystemOption')->get('shareLinkAllowEdit') == '0'){$canEdit = false;}
			if(!$canEdit){$this->showError(LNG('explorer.noPermissionWriteAll'),false);}
		}
		
		// 下载计数; 分片下载时仅记录起始为0的项(并忽略长度为0的请求),忽略head请求;
		if($isDownload){
			if(!Action('admin.log')->checkHttpRange()){return;}
			$this->model->where($where)->setAdd('numDownload');
			$this->cacheShareClear();
		}
	}
	private function checkPathAuth($path,$isArray = false){
		if(!$this->share || !$this->share['shareHash']){return false;}
		$prePath = "{shareItemLink:".$this->share['shareHash']."}";
		if(!$isArray){return strpos(rawurldecode($path),$prePath) === 0 ? true:false;}
		
		$data = json_decode($this->in['dataArr'],true);
		if(!is_array($data)){return false;}
		foreach ($data as $item){
			if(strpos(rawurldecode($item['path']),$prePath) !== 0){return false;}
		}
		return true;
	}
	
	// 清除分享方法缓存
	private function cacheShareClear(){
		$this->model->cacheFunctionClear('listSimple', $this->share['userID']);
		$this->model->cacheFunctionClear('getInfoShare', $this->share['shareID']);
	}
	/**
	 * 检测并获取真实路径;
	 */
	private function parsePath($path,$allowNotExist=false){
		if(request_url_safe($path)) return $path;//压缩包支持;
		$rootSource = $this->share['sourceInfo']['path'];
		$parse = KodIO::parse($path);
		if(!$parse || $parse['type']  != KodIO::KOD_SHARE_LINK ||
			$this->share['shareHash'] != $parse['id'] ){
			show_json(LNG('common.noPermission'),false);
		}
		
		$pathInfo = IO::infoFullSimple($rootSource.$parse['param']);
		if(!$pathInfo){
			if($allowNotExist){return $rootSource.$parse['param'];}
			show_json(LNG('common.pathNotExists'),false);
		}
		return $pathInfo['path'];
	}
	
	public function pathInfo(){
		$fileList = json_decode($this->in['dataArr'],true);
		if(!$fileList) show_json(LNG('explorer.error'),false);

		$result = array();
		for ($i=0; $i < count($fileList); $i++) {
			$path = $this->parsePath($fileList[$i]['path']);
			$this->fileCheckDownload($path);
			if($this->in['getChildren'] == '1'){
				$pathInfo = IO::infoWithChildren($path);
			}else{
				$pathInfo = IO::infoFull($path);
			}
			if(!is_array($pathInfo)){continue;}
			if(!array_key_exists('hasFolder',$pathInfo)){
				$has = IO::has($path,true);
				if(is_array($has)){$pathInfo = array_merge($pathInfo,$has);}
			}
			
			$pathInfoOut = $this->shareItemInfo($pathInfo);
			if(is_array($pathInfo['fileInfo'])){$pathInfoOut['fileInfo'] = $pathInfo['fileInfo'];} // 用户hash获取等;
			$pathInfoOut = Action('explorer.list')->pathInfoCover($pathInfoOut);//path参数需要是外链路径;
			if(is_array($pathInfoOut['fileInfo'])){unset($pathInfoOut['fileInfo']);}
			$result[] = $pathInfoOut;
		}
		
		if(count($fileList) == 1){
			// 单个文件属性; 没有仅用预览,则开放预览链接;
			$canDownload = _get($this->share,'options.notDownload') != '1';
			if($this->shareOuterAuth){$canDownload = true;}
			if($result[0]['type'] == 'file' && $canDownload ){
				$param = "shareID=".$this->share['shareHash']."&path=".rawurlencode($result[0]['path']);
				$param .= '&name=/'.rawurlencode($result[0]['name']);
				$result[0]['downloadPath'] = urlApi('explorer/share/fileOut',$param);
			}
			show_json($result[0]);
		}
		show_json($result);		
	}

	//输出文件
	public function fileOut(){
		$path = rawurldecode($this->in['path']);//允许中文空格等;
		if(request_url_safe($path)){header('Location:'.$path);exit;}
		
		$path = $this->parsePath($path);
		$this->fileCheckDownload($path);
		$isDownload = $this->in['download'] == '1';
		Hook::trigger('explorer.fileOut', $path);
		if(isset($this->in['type']) && $this->in['type'] == 'image'){
			$info = IO::info($path);
			$imageThumb = array('jpg','png','jpeg','bmp');
			$width = isset($this->in['width']) ? intval($this->in['width']) :0;
			if($isDownload || !$width || $width >= 2000){Action('explorer.index')->updateLastOpen($path);}
			if($info['size'] >= 1024*200 &&
				function_exists('imagecolorallocate') &&
				in_array($info['ext'],$imageThumb) 
			){return IO::fileOutImage($path,$this->in['width']);}
		}
		Action('explorer.index')->fileOutUpdate($path,$isDownload);
	}
	
	public function fileDownload(){
		$this->in['download'] = 1;
		$this->fileOut();
	}
	public function fileOutBy(){
		$add   = kodIO::pathUrlClear(rawurldecode($this->in['add']));
		$path  = rawurldecode($this->in['path']);
		if(request_url_safe($path)){header('Location:'.$path);exit;}
		
		$distPath = kodIO::pathTrue($path.'/../'.$add);
		$this->in['path'] = rawurlencode($distPath);
		if(isset($this->in['type']) && $this->in['type'] == 'getTruePath'){
			show_json($distPath,true);
		}
		$this->fileOut();
	}
	
	private function call($action,$parsePath=false){
		$self = $this;
		if(isset($this->in['path']) && !$parsePath){
			$this->in['path'] = $this->parsePath($this->in['path'],true);
		}
		ActionCallResult($action,function(&$res) use($self){
			if(!$res['code']){return;}
			if(ACT == 'fileSave'){$res['info'] = $this->shareItemInfo($res['info']);return;}
			
			// 粘贴,拖拽情况处理;
			if(ACT == 'pathCuteTo' || ACT == 'pathCopyTo' || ACT == 'pathPast'){
				$resNew = $self->callParsePast($res);
				if($resNew && $resNew['info']){$res['info'] = $resNew['info'];}
				if($resNew && $resNew['infoMore']){$res['infoMore'] = $resNew['infoMore'];}
				return;
			}
			
			if(!$res['info'] || !is_string($res['info'])){return;}
			$pathInfo = $self->shareItemInfo(IO::info($res['info']));
			$res['info'] = $pathInfo['path'];
		});		
	}
	public function fileUpload(){$this->call("explorer.upload.fileUpload");}
	public function mkfile(){$this->call("explorer.index.mkfile");}
	public function mkdir(){$this->call("explorer.index.mkdir");}
	public function pathDelete(){$this->call("explorer.index.pathDelete");}
	public function pathRename(){$this->call("explorer.index.pathRename");}
	
	// path,pathArr都不处理路径为source,确保io一致,否则source内容外链分享:新建再删除,会产生脏数据
	public function pathCuteTo(){$this->call("explorer.index.pathCuteTo",true);}
	public function pathCopyTo(){$this->call("explorer.index.pathCopyTo",true);}
	public function pathCute(){$this->call("explorer.index.pathCute",true);}
	public function pathCopy(){$this->call("explorer.index.pathCopy",true);}
	public function pathPast(){$this->call("explorer.index.pathPast");}
	public function fileSave(){$this->call("explorer.editor.fileSave");}
	
	public function callParsePast($res){
		if(!is_array($res['info']) || !isset($res['info'][0])){return false;}
		foreach($res['info'] as $i=>$path){
			$pathInfo = $this->shareItemInfo(IO::info($res['info'][$i]));
			$res['info'][$i] = $pathInfo['path'];
			if(is_array($res['infoMore']['listTo'])){
				$res['infoMore']['listTo'][$i] = $pathInfo['path'];
			}
		}
		if(is_array($res['infoMore']) && isset($res['infoMore']['pathTo'])){
			$res['infoMore']['pathTo'] = $_REQUEST['path'];
		}
		return $res;
	}
	
	public function fileGet(){
		$pageNum = 1024 * 1024 * 10;$self = $this;
		$this->in['pageNum'] = isset($this->in['pageNum']) ? $this->in['pageNum'] : $pageNum;
		$this->in['pageNum'] = $this->in['pageNum'] >= $pageNum ? $pageNum : $this->in['pageNum'];
		$this->in['path'] = $this->parsePath($this->in['path']);
		$this->fileCheckDownload($this->in['path']);
		ActionCallResult("explorer.editor.fileGet",function(&$res) use($self){
			if(!$res['code']){return;}
			$pathDisplay = $res['data']['pathDisplay'];
			$res['data'] = $self->shareItemInfo($res['data']);
			if($pathDisplay && substr($pathDisplay,0,1) == '['){$res['data']['pathDisplay'] = $pathDisplay;}
			if(is_array($res['data']['fileInfo'])){unset($res['data']['fileInfo']);}
		});
	}
	
	// 文件下载权限校验
	public function fileCheckDownload($path){
		$parse  = KodIO::parse($path);
		if($parse['type'] == KodIO::KOD_SOURCE){ // 上层包含密码时处理;
			$errorMsg = Action('explorer.listPassword')->authCheck(IO::info($path),'download');
			if($errorMsg){show_json($errorMsg,false,1102);}
		}
	}
	
	// 压缩包内文本文件请求(不再做权限校验; 通过文件外链hash校验处理)
	public function fileGetHash(){
		$pageNum = 1024 * 1024 * 10;
		$this->in['pageNum'] = isset($this->in['pageNum']) ? $this->in['pageNum'] : $pageNum;
		$this->in['pageNum'] = $this->in['pageNum'] >= $pageNum ? $pageNum : $this->in['pageNum'];
		// ActionCall("explorer.editor.fileGet");exit;

		$url = $this->in['path'];
		$urlInfo = parse_url_query($url);
		if( !isset($urlInfo["explorer/share/unzipListHash"]) && 
			!isset($urlInfo["accessToken"])){
			show_json(LNG('common.pathNotExists'),false);
		}
		$index 	  = json_decode(rawurldecode($urlInfo['index']),true);
		$zipFile  = $this->fileHash(rawurldecode($urlInfo['path']));
		$filePart = IOArchive::unzipPart($zipFile,$index ? $index:'-1');
		if(!$filePart || !IO::exist($filePart['file'])){
			show_json(LNG('common.pathNotExists'),false);
		}
		Action("explorer.editor")->fileGetMake($filePart['file'],IO::info($filePart['file']),$url);
	}
	
	public function pathList(){
		$path = $this->in['path'];
		$pathParse = KodIO::parse($path);
		if($pathParse['type'] == KodIO::KOD_SEARCH){
			$searchParam = Action('explorer.listSearch')->parseSearch($pathParse['param']);
			$this->parsePath($searchParam['parentPath']); //校验path;
			$data = Action('explorer.listSearch')->listSearchPath($pathParse);
			Action('explorer.list')->pageParse($data);
			Action('explorer.list')->pathListParse($data);
			$data['current']  = Action('explorer.list')->pathCurrent($path);
			$data['thisPath'] = $path;
		}else{
			$allowPath = explode(',','{block:fileType}'); //允许的目录;虚拟目录;
			if(!in_array($path,$allowPath)){$this->parsePath($path);}
			$data = Action('explorer.list')->path($path);
		}
		
		// 默认自动展开文件夹层级; deep=1/2(文档预览模式,优化请求)
		if(isset($this->in['deep']) && is_array($data['folderList']) && 
			$this->in['deep'] >= 1){
			foreach ($data['folderList'] as $key=>$item){
				$child = Action('explorer.list')->path($item['path']);
				$item['children'] = $child;$data['folderList'][$key] = $item;
				if($this->in['deep'] == 1 || !is_array($child['folderList'])){continue;}

				foreach($child['folderList'] as $key2=>$item2) {
					$child2 = Action('explorer.list')->path($item2['path']);
					$item2['children'] = $child2;
					$data['folderList'][$key]['children']['folderList'][$key2] = $item2;
				}
			}
		}
		
		// 文件快捷方式处理;
		if($data && $data['fileList']){
			foreach($data['fileList'] as &$file){
				if(is_array($file['fileInfo'])){unset($file['fileInfo']);}
				$this->filterOexeContent($file);
			}
		}		
		show_json($data);
	}
	
	/**
	 * 分享压缩下载
	 * 压缩和下载合并为同一方法
	 */
	public function zipDownload(){
		$dataArr = json_decode($this->in['dataArr'],true);
		if($this->in['zipClient'] == '1'){
			$result = Action('explorer.index')->zipDownloadClient($dataArr);
			$sharePath = $this->share['sourcePath'];
			$shareLink = "{shareItemLink:".$this->share['shareHash']."}/";
			foreach($result as $i => $item){
				if(!$item['filePath']){continue;}
				$result[$i]['filePath'] = str_replace($sharePath,$shareLink,$item['filePath']);
			}
			show_json($result,true);return;
		}
		$this->zipSupportCheck();
		foreach($dataArr as $i => $item){
			$dataArr[$i]['path'] = $this->parsePath($item['path']);
		}
		$this->in['dataArr'] = json_encode($dataArr);
		Action('explorer.index')->zipDownload();
	}
	public function fileDownloadRemove(){
		Action('explorer.index')->fileDownloadRemove();
	}
	public function unzipList(){
		$this->zipSupportCheck();
		$this->in['path'] = $this->parsePath($this->in['path']);
		Action('explorer.index')->unzipList();
	}
	public function unzipListHash(){
		$this->zipSupportCheck();
		$this->in['path'] = $this->fileHash($this->in['path']);
		Action('explorer.index')->unzipList();
	}

	private function zipSupportCheck(){
		if(Model('SystemOption')->get('shareLinkZip') == '1') return true;
		show_json(LNG('admin.setting.shareLinkZipTips'),false);
	}
	

	public function shareItemInfo($item){
		$rootPath = $this->share['sourceInfo']['pathDisplay'];
		// 物理路径,io路径;
		if($this->share['sourceID'] == '0'){
			$rootPath = KodIO::clear($this->share['sourcePath']);
			
			// io路径(本地磁盘挂载时); 外链分享搜索时会处理为真实路径情况处理;
			if(substr($item['path'],0,strlen($rootPath)) != $rootPath){
			    $io = IO::init($rootPath);
			    $rootPath = $io->path;
			}
		}
		$item['pathDisplay'] = $item['pathDisplay'] ? $item['pathDisplay']:$item['path'];

		$field = array(
			'name','path','type','size','ext','searchTextFile',
			'createUser','modifyUser','createTime','modifyTime','sourceID','parentLevel',
			'hasFolder','hasFile','children','targetType','targetID','pageInfo','searchContentMatch',
			'base64','content','charset','oexeContent','oexeSourceInfo','fileInfoMore','fileInfo','fileThumb',
			// 'isReadable','isWriteable',//(不处理, 部门文件夹分享显示会有区分)
		);
		$theItem = array_field_key($item,$field);
		$path 	 = KodIO::makePath(KodIO::KOD_SHARE_LINK,$this->share['shareHash']);
		$name    = $this->share['sourceInfo']['name'];
		$theItem['pathDisplay'] = ltrim(substr($item['pathDisplay'],strlen($rootPath)),'/');
		$theItem['path'] = rtrim($path,'/').'/'.$theItem['pathDisplay'];
		$theItem['pathDisplay'] = $name.'/'.$theItem['pathDisplay'];

		if(is_array($item['metaInfo'])){
			$picker = 'user_sourceCover,folderPassword';
			$theItem['metaInfo'] = array_field_key($item['metaInfo'],explode(',',$picker));
		}
		if($theItem['type'] == 'folder'){$theItem['ext'] = 'folder';}
		if(is_array($theItem['createUser'])) $theItem['createUser'] = $this->filterUserInfo($theItem['createUser']);
		if(is_array($theItem['modifyUser'])) $theItem['modifyUser'] = $this->filterUserInfo($theItem['modifyUser']);
		return $theItem;
	}
	
	private function filterOexeContent(&$theItem){
		if($theItem['type'] != 'file' || $theItem['ext'] != 'oexe') return;
		if(!isset($theItem['oexeContent'])) return;
		if($theItem['oexeContent']['type'] != 'path') return;
		
		$rootPath = $this->share['sourceInfo']['pathDisplay'];
		if($this->share['sourceID'] == '0'){
			$rootPath = KodIO::clear($this->share['sourcePath']);
			$pathDisplay = $theItem['oexeContent']['value'];
		}else{
			$sourceInfo  = IO::info($theItem['oexeContent']['value']);
			$pathDisplay = $sourceInfo['pathDisplay'];
		}
		$path = KodIO::makePath(KodIO::KOD_SHARE_LINK,$this->share['shareHash']);
		$pathDisplay = ltrim(substr($pathDisplay,strlen($rootPath)),'/');
		$theItem['oexeContent']['value'] = rtrim($path,'/').'/'.$pathDisplay;
		
		if($sourceInfo){
			$sourceInfo['path'] = $theItem['oexeContent']['value'];
			$theItem['oexeSourceInfo'] =  $sourceInfo;
		}
	}
	private function filterUserInfo($userInfo){
		$name = !empty($userInfo['nickName']) ? $userInfo['nickName'] : $userInfo['name'];
		unset($userInfo['nickName'], $userInfo['name']);
		$userInfo['nameDisplay'] = $this->parseName($name);
		$userInfo['userID'] = 'user:'.$userInfo['userID'];
		return $userInfo;
	}
	private function parseName($name){
		$len = mb_strlen($name);
		return $len > 3 ? mb_substr($name,0,3).'***':$name;
	}

	/**
	 * 分享文件举报
	 * @return void
	 */
	public function report(){
		$data = Input::getArray(array(
			'path'	=> array('check' => 'require'),
			'type'	=> array('check' => 'in', 'param' => array('1','2','3','4','5')),
			'desc'	=> array('default' => '')
		));
		$fileID = 0;
		if($this->share['sourceInfo']['type'] == 'file') {
			$info = $this->sharePathInfo($data['path']);
			$fileID = $info['fileInfo']['fileID'];
		}
		$data['shareID']	= $this->share['shareID'];
		$data['sourceID']	= $this->share['sourceID'];
		$data['title']		= $this->share['title'];
		$data['fileID']		= $fileID;
		$res = $this->model->reportAdd($data);
		show_json('OK', !!$res);
	}
}
shareOut.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/shareOut.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

// 对外站点联合分享处理;
class explorerShareOut extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$GLOBALS['config']['jsonpAllow'] = true;
	}
	
	// 检测是否允许接收外站联合分享; 分享发起端,前端添加编辑用户时检测
	public function shareCheck(){
		$data = $this->shareParamParse();
		if(!$data['_authTo']){show_json(LNG('explorer.shareOut.errorTarget'),false);}
		show_json("ok:check",true);
	}
	// 接收处理: 自动生成协作分享--独立IO;个人中心显示与我协作;
	public function shareMake(){
		$data  = $this->shareParamParse();
		$model = Model('Share');
		
		$siteFrom   = rtrim(get_url_link($data['siteFrom']),'/');
		$sourcePath = 'share@'.intval($data['shareID']).'@'.$siteFrom;
		$shareFind  = $model->where(array('userID'=>0,'sourcePath'=>$sourcePath))->find();
		if(!$data['_authTo']){
			if($shareFind){$model->remove($shareFind['shareID']);}
			if(!$data['shareOut']){show_json('ok',true);}
			show_json(LNG('explorer.shareOut.errorTarget'),false);
		}
		
		// 检测来源站点,并且检测否启用了外部站点分享功能;
		$apiFrom  = $siteFrom.'/index.php?explorer/shareOut/sendCheckAllow';
		$response = url_request($apiFrom,'GET',array(),false,false,true,6);
		if(_get($response,'data.info') != 'kodbox'){// 不是kodbox站点输出则忽略;
			show_json(LNG('explorer.shareOut.errorNetwork').",".$siteFrom,false);
		}else if(!_get($response,'data.code')){
			show_json(_get($response,'data.data'),false);
		}
		
		$saveData = array(
			'isLink'	=> '0','isShareTo' => '1',
			'password'	=> '__SHSRE_OUTER__',
			'sourcePath'=> $sourcePath,
			'authTo' 	=> $data['_authTo'],
			'options'	=> array(
				'site'			=> $siteFrom,
				'shareID'   	=> $data['shareID'],
				'shareHash' 	=> $data['shareHash'],
				'shareUser' 	=> $data['shareUser'],
				'sourceInfo'	=> $data['sourceInfo'],
				'shareTarget'	=> $data['_shareTarget'],
			),
		);
		if($shareFind){
			$model->shareEdit($shareFind['shareID'],$saveData);
			$shareID = $shareFind['shareID'];
		}else{
			$shareID = $model->shareAddSystem(0,$saveData);
			$model->shareEdit($shareID,$saveData);
		}
		// show_json([$model->getInfo($shareID),$model->getInfo($data['shareID']),$saveData,$data]);
		show_json("ok:".($shareFind ? 'edit':'add').';shareID='.$shareID,true);
	}
	
	// 获取分享对象信息; 对象类型/对象id/权限id;
	private function shareParamParse(){
		$data = Input::getArray(array(
			'_check'	=> array("check"=>"require"),
			'siteFrom'	=> array("check"=>"url"),
			'siteTo'	=> array("check"=>"url"),
			'shareID'	=> array("check"=>"number"),
			'shareHash'	=> array("check"=>"require"),
			'shareUser'	=> array("check"=>"require"),
			'sourceInfo'=> array("check"=>"json","default"=>array()),
			'shareOut'	=> array("check"=>"json","default"=>array()),// [{target,auth},...]
		));
		$this->tableInit();
		$config 	= Model('SystemOption')->get();
		$checkKey 	= Mcrypt::decode($data['_check'],'kodShareOut');
		$limitKey   = 'shareOutRecive-'.$data['siteFrom'];
		if($config['shareOutAllowRecive'] != '1'){show_json(LNG('explorer.shareOut.errorDisableReceive'),false);}
		if($data['siteFrom'] != $checkKey){show_json(LNG('explorer.share.errorParam'),false);}
		if(!Cache::limitCall($limitKey,5,10,false)){show_json(LNG('explorer.shareOut.errorCallLimit'),false);}
		
		$authList = $this->authListMake();
		$data['_shareTarget'] = array();$data['_authTo'] = array();
		$dataAdd = array();
		foreach($data['shareOut'] as $item){
			$target 	= $item['target'];
			$targetType = SourceModel::TYPE_USER;$targetID = 0;
			if(substr($target,0,strlen('user:')) === 'user:'){
				$dataInfo = Model('User')->getInfoSimple(substr($target,strlen('user:')));
				if($dataInfo){$targetID = $dataInfo['userID'];}
			}else if(substr($target,0,strlen('group:')) === 'group:'){
				$dataInfo = Model('Group')->getInfoSimple(substr($target,strlen('group:')));
				if($dataInfo){$targetID = $dataInfo['groupID'];$targetType = SourceModel::TYPE_GROUP;}
			}else{
				$dataInfo = Model('User')->userLoginFind($target);
				if($dataInfo){$targetID = $dataInfo['userID'];}
			}
			$authID = $authList[$item['auth']];
			$dataAddKey = $targetType.'-'.$targetID;// 去重处理;
			if(!$targetID || !$authID || $dataAdd[$dataAddKey]){continue;}
			
			$dataAdd[$dataAddKey] = true;
			$_item = array_field_key($item,array('target','secret','auth'));
			$data['_shareTarget'][] = array_merge($_item,array('authID'=>$authID,'to'=>$target.'@'.$data['siteTo']));
			$data['_authTo'][] = array('targetType'=>$targetType,'targetID'=>$targetID,"authID"=>$authID);
		}
		return $data;
	}
	
	// 权限映射到当前站点文档权限;
	private function authListMake(){
		return array(
			'read' => Model('Auth')->findAuthMax(
				AuthModel::AUTH_SHOW | AuthModel::AUTH_VIEW | AuthModel::AUTH_DOWNLOAD,
				AuthModel::AUTH_UPLOAD | AuthModel::AUTH_EDIT | AuthModel::AUTH_REMOVE
			),
			'write' => Model('Auth')->findAuthMax(
				AuthModel::AUTH_SHOW | AuthModel::AUTH_VIEW | AuthModel::AUTH_DOWNLOAD | AuthModel::AUTH_UPLOAD | AuthModel::AUTH_EDIT | AuthModel::AUTH_REMOVE,
				AuthModel::AUTH_ROOT
			),
		);
	}
	
	// apiKey检测(管理员后台,添加授信站点时前端发起请求)
	public function shareCheckApiKey(){
		$config 	= Model('SystemOption')->get();
		$checkKey 	= Mcrypt::decode($this->in['apiKey'],'kodShareOut');
		if($config['shareOutAllowRecive'] != '1'){show_json(LNG('explorer.shareOut.errorDisableReceive'),false);}
		if($config['shareOutSiteApiKey']  != $checkKey){show_json(LNG('explorer.shareOut.errorApiKey'),false);}
		show_json("ok:check",true);
	}
	// 授信目标站点组织架构获取;分享时前端发起获取;
	public function shareSafeGroup(){
		$config 	= Model('SystemOption')->get();		
		$checkKey 	= Mcrypt::decode($this->in['sk'],$config['shareOutSiteApiKey']);
		if($config['shareOutAllowRecive'] != '1'){show_json(LNG('explorer.shareOut.errorDisableReceive'),false);}
		if($checkKey != 'kodShareOutGroup'){show_json(LNG('explorer.shareOut.errorApiKey').'(timeout)',false);}
		
		$GLOBALS['isRoot'] = true;
		$methodMap = array(
			'groupList'		=> array('action'=>'admin.group.get','param'=>'parentID'),
			'groupSearch'	=> array('action'=>'admin.group.search','param'=>'words'),
			'memberList'	=> array('action'=>'admin.member.get','param'=>'groupID'),
			'memberSearch'	=> array('action'=>'admin.member.search','param'=>'words'),
		);
		$methodInfo = $methodMap[$this->in['method']];
		if(!$methodInfo){show_json('method error!',false);}
		ActionCallResult($methodInfo['action'],function(&$res) use($self){
			if(!$res['code']){return;}
			$index = $GLOBALS['in']['siteIndex'];
			$fieldGroup = array('groupID','name','groupPath','hasChildren','hasChildrenMember');
			$fieldUser  = array('userID','name','nickName','avatar');
			foreach($res['data']['list'] as $i=>$item){
				if($item['groupID']){$item = array_field_key($item,$fieldGroup);}
				if($item['userID']){$item = array_field_key($item,$fieldUser);}
				$item['siteIndex'] = $GLOBALS['in']['siteIndex'];
				$res['data']['list'][$i] = $item;
			}
		});
	}
	
	// 接收端, 成员用户退出协作处理
	public function shareUserExit($siteFrom,$shareID,$secret){
		if(!$siteFrom || !$shareID || !$secret){return false;}
		$apiFrom 	= $siteFrom.'/index.php?explorer/shareOut/sendShareUserExit';
		$param 		= array('shareID'=>$shareID,'secret'=>$secret);
		$response 	= url_request($apiFrom,'POST',$param,false,false,true,3);
		return $response['data'];
	}
	
	//==================== 作为[联合分享发送方]提供的接口 ===================
	
	// 外部用户访问权限校验; return: false/read/write; (接收端内部IO向发起端请求, 发起端校验处理);
	// 校验通过返回对应权限; 不通过则默认使用外链分享各个限制;
	public function sendCheckAuth($shareInfo){
		if(!$this->in['sk']){return false;}
		if(Model('SystemOption')->get('shareOutAllowSend') != '1'){return false;}
		$decode 	= Mcrypt::decode($this->in['sk'],'kodShareOut').'';
		$dataArr 	= explode('$@$',$decode);
		$authTo 	= array_find_by_field($shareInfo['options']['shareOut'],'to',$dataArr[1]);
		if(!is_array($authTo) || $authTo['secret'] != $dataArr[0]){return false;}
		$GLOBALS['fileEncodeDisable'] = '1';// 屏蔽外链分享,文件内容加密(文档水印及安全--启用文件加密启用时)
		return $authTo['auth'];
	}
	// 接收时向发起站点做访问校验处理; 接收端接收到推送时向发起端后端请求(shareMake中);
	public function sendCheckAllow(){
		$config = Model('SystemOption')->get();
		if($config['shareLinkAllow'] != '1'){show_json(LNG('explorer.shareOut.errorDisableShare'),false,'kodbox');}
		if($config['shareOutAllowSend'] != '1'){show_json(LNG('explorer.shareOut.errorDisableSend'),false,'kodbox');}
		show_json("ok:check",true,'kodbox');
	}
	
	// 分享端,接收成员用户退出协作处理;
	public function sendShareUserExit(){
		$config = Model('SystemOption')->get();
		if($config['shareLinkAllow'] != '1'){show_json(LNG('explorer.shareOut.errorDisableShare'),false,'kodbox');}
		if($config['shareOutAllowSend'] != '1'){show_json(LNG('explorer.shareOut.errorDisableSend'),false,'kodbox');}
		$data = Input::getArray(array(
			'shareID'	=> array("check"=>"number"),
			'secret'	=> array("check"=>"require"),
		));
		$model 		= Model('Share');
		$shareInfo 	= $model->getInfo($data['shareID']);
		$shareOut 	= _get($shareInfo,'options.shareOut',array());
		$shareOutNew = array();$hasFind = false;
		foreach($shareOut as $item){
			if($item['secret'] != $data['secret']){$shareOutNew[] = $item;continue;}
			$hasFind = true;
		}
		if(!$hasFind){show_json(LNG('explorer.share.notExist'),false,'kodbox');}
		
		$shareOptions = $shareInfo['options'];
		$shareOptions['shareOut'] = $shareOutNew;
		if(!$shareOutNew){unset($shareOptions['shareOut']);}
		$model->shareEdit($data['shareID'],array('isLink'=>'1','options'=>$shareOptions));
		$model->cacheFunctionClear('listSimple',$shareInfo['userID']);// 自己分享列表信息更新;
		show_json('ok',true,'kodbox');
	}
	
	// 当前站点分享信息获取时,授信站点列表获取(apiKey加密); 供前端显示用(前端获取用户部门信息)
	public function sendShareSiteAppend($shareInfo){
		$config = Model('SystemOption')->get();
		$this->tableInit();
		if(!$shareInfo || !is_array($shareInfo)){return $shareInfo;}
		if($config['shareLinkAllow'] != '1' || $config['shareOutAllowSend'] != '1'){return $shareInfo;}
		$result   = array();$listAdd = array();
		$siteList = json_decode(_get($config,'shareOutSiteSafe',''),true);
		if(!is_array($siteList)){$siteList = array();}
		foreach($siteList as $site){
			if(!$site || $site['isOpen'] != '1' || !$site['apiKey']){continue;}
			if($listAdd[$site['url']]){continue;} // 去重;
			$result[] = array(
				'url' 	=> $site['url'],
				'name'	=> $site['name'],
				'sk' 	=> Mcrypt::encode('kodShareOutGroup',$site['apiKey'],600),//有效期10分钟;
			);
			$listAdd[$site['url']] = true;
		}
		$shareInfo['shareOutSite'] = $result;
		return $shareInfo;
	}
	
	// 表结构初始化; varchar(1000)=>text (65536字符);
	private function tableInit(){
		if(Model('SystemOption')->get('shareOutInit')){return;}
		Model('SystemOption')->set('shareOutInit','1');
		$sql = "ALTER TABLE `share` CHANGE `options` `options` text COLLATE 'utf8_general_ci' NULL COMMENT 'json 配置信息' AFTER `numDownload`;";
		if(stristr($this->config['database']['DB_TYPE'],'sqlite')){$sql = '';}
		if($sql){Model()->db()->execute($sql);}
	}
}
tag.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/tag.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 标签管理:增删改查、置顶置底;
 * listData();				//tag列表 
 * add();					//tag添加   [参数]:name/style
 * edit();					//重命名Tag [参数]:tagID,name/style
 * remove();				//删除tag 	[参数]:tagID
 * moveTop();				//置顶 		[参数]:tagID
 * moveBottom();			//置底 		[参数]:tagID
 * resetSort();				//重置排序,更具id的顺序重排; [参数]:tagList:逗号隔开的id
 * -------
 * sourceAddToTag();		//添加文档到tag [参数]:tagID/sourceID
 * sourceResetTag();		//重置某个文档所在的tag [参数]:tagList:逗号隔开的id/sourceID
 * sourceRemoveFromTag();	//将文档从某个tag中移除 [参数]:tagID/sourceID
 */
class explorerTag extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  		= Model('UserTag');
		$this->modelSource  = Model('UserTagSource');
	}
	/**
	 * tag列表
	 */
	public function get() {
		show_json($this->data());
	}
	private function data(){
		return $this->model->listData();
	}
	/**
	 * 用户文件标签列表
	 */
	public function tagList(){
		$this->initUserData();
		$dataList = $this->data();
		$tagSource = $this->modelSource->listData();
		$list = array();
		foreach ($dataList as $item) {
			$style  = $item['style']? $item['style'] : 'label-grey-normal';
			$find   = array_filter_by_field($tagSource,'tagID',$item['id']);
			$list[$item['id']] = array(
				"name"		=> $item['name'],
				"path"		=> KodIO::makeFileTagPath($item['id']),
				"icon"		=> 'tag-label label ' . $style,
				'tagInfo' 	=> $item,
				'tagHas' 	=> count($find),
				'pathDesc'  => LNG('explorer.pathDesc.tagItem'),
			);
		}
		return $list;
	}
	
	private function initUserData(){
		if(Model('UserOption')->get('userTagInit','flag') == 'ok') return;
		$list = $GLOBALS['config']['settings']['userDefaultTag'];
		foreach ($list as $item) {
			$this->model->add(LNG($item['name']),$item['style']);
		}
		Model('UserOption')->set('userTagInit','ok','flag');
	}
	
	public function listSource($tags){
		$groupPre = 'group-';
		if(strstr($tags,$groupPre)){
			$tags = explode('-',substr($tags,strlen($groupPre)));
			return Action("explorer.tagGroup")->listSource($tags[0],array_slice($tags,1));
		}
		
		$tags   = explode('-',$tags);
		$tags   = $tags[0] ? $tags : false;
		$result = Model("Source")->listUserTag($tags);
		$tagInfo= $this->tagsInfo($tags);
		$tagInfo['pathAddress'] = array(
			array("name"=> LNG('explorer.userTag.title'),"path"=>'{block:fileTag}/'),
			array("name"=> $tagInfo['name'],"path"=>$this->in['path']),
		);
		$tagInfo['pathDesc'] = LNG('explorer.tag.pathDesc');
		if(!$result){$result = array("fileList"=>array(),'folderList'=>array());}
		$result['currentFieldAdd'] = $tagInfo;
		$result['fileList']   = $this->_checkExists($result['fileList']);
		$result['folderList'] = $this->_checkExists($result['folderList']);
		return $result;
	}
	
	// 自动清理不存在的内容(仅限物理路径, 其他io路径由于比较耗时暂不处理,手动处理)
	private function _checkExists($list){
		if(!$list){return array();}
		foreach($list as $key => &$item){
			if(substr($item['path'],0,1) == '{'){continue;} // 仅处理物理路径;
			if(!file_exists($item['path'])){
				// $item['exists'] = false;
				unset($list[$key]);
				$this->modelSource->removeBySource($item['path']);
			}
		};unset($item);
		return $list;
	}
	
	// 普通路径追加标签信息; source路径会已经自动添加;
	public function tagAppendItem(&$item){
		if(isset($item['sourceInfo']['tagInfo'])){return $item;}
		
		static $listPathMap = false;
		static $tagInfoArr  = false;
		if($listPathMap === false){ // 缓存处理;
			$this->modelSource->cacheFunctionClear('listData',USER_ID);
			$listPathMap = $this->modelSource->listData(); // model查询缓存;			
			$listPathMap = array_to_keyvalue_group($listPathMap,'path','tagID');
			$tagInfoArr  = $this->model->listData();
			$tagInfoArr  = array_to_keyvalue($tagInfoArr,'id');
		}
		if(!$tagInfoArr || !$listPathMap){return $item;}
		
		$item['sourceInfo']['tagInfo'] = 0;
		$path 	 = $item['path'];$path1 = rtrim($item['path'],'/');$path2 = rtrim($item['path'],'/').'/';
		$findItem = isset($listPathMap[$path]) ? $listPathMap[$path]:false;
		$findItem = (!$findItem && isset($listPathMap[$path1])) ? $listPathMap[$path1]:$findItem;
		$findItem = (!$findItem && isset($listPathMap[$path2])) ? $listPathMap[$path2]:$findItem;
		if(!$findItem){return $item;}
		
		$item['sourceInfo']['tagInfo'] = array();
		foreach ($findItem as $tagID) {
			$item['sourceInfo']['tagInfo'][] = array(
				"tagID"	=> $tagInfoArr[$tagID]['id'],
				"name"	=> $tagInfoArr[$tagID]['name'],
				"style"	=> $tagInfoArr[$tagID]['style'],
			);
		}
		return $item;
	}
	
	
	private function tagsInfo($tags){
		$info = false;
		$styleDefault = 'tag-label label label-blue-normal';
		if(!$tags){return array('name'=>'--','icon'=>$styleDefault);}
		
		$tagList = $this->model->listData();
		foreach ($tagList as $item) {
			$icon = 'tag-label label '.$item['style'];
			if(!in_array($item['id'],$tags)) continue;
			if(!$info){$info = array('name'=>$item['name'],'icon'=>$icon);continue;}
			$info['name'] .= ','.$item['name'];
		}
		if(count($tags) > 1){$info['icon'] = $info['icon'].' label-mutil';}
		return $info;
	}
	

	/**
	 * tag添加
	 */
	public function add(){
		$data = Input::getArray(array(
			"name"		=> array("check"=>"require"),
			"style"		=> array('check'=>"require"),
		));
		if(count($this->data()) > $GLOBALS['config']['systemOption']['tagNumberMax']){
			show_json(LNG("common.numberLimit"),false);
		}
		
		$res = $this->model->add($data['name'],$data['style']);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.repeatError');
		show_json($msg,!!$res,$this->data());
	}

	/**
	 * 重命名Tag
	 */
	public function edit() {
		$data = Input::getArray(array(
			"tagID"		=> array("check"=>"int"),
			"name"		=> array('default'=>null),
			"style"		=> array('default'=>null),
		));
		$res = $this->model->update($data['tagID'],$data);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.repeatError');
		show_json($msg,!!$res,$this->data());
	}
	
	/**
	 * 删除tag
	 */
	public function remove(){
		$tagID = Input::get('tagID',"int");
		$res = $this->model->remove($tagID);
		$this->modelSource->removeByTag($tagID);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res,$this->data());
	}
	
	/**
	 * 置顶
	 */
	public function moveTop() {
		$tagID = Input::get('tagID',"int");
		$res = $this->model->moveTop($tagID);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res,$this->data());
	}

	/**
	 * 置底
	 */
	public function moveBottom() {
		$tagID = Input::get('tagID',"int");
		$res = $this->model->moveBottom($tagID);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res,$this->data());
	}
	/**
	 * 重置排序,根据id的顺序重排;
	 */
	public function resetSort() {	
		$idList = Input::get('tagList',"require");
		$idArray = explode(',',$idList);
		if(!$idArray) {
			show_json(LNG('explorer.error'),false);
		}
		$res = $this->model->resetSort($idArray);
		$msg = $res ? LNG('explorer.success') : LNG('explorer.error');
		show_json($msg,!!$res,$this->data());
	}

	
	//======== tag关联资源管理 =========
	//将文档从某个tag中移除
	public function filesRemoveFromTag(){
		$data = Input::getArray(array(
			"tagID"	=> array("check"=>"int"),
			"files"	=> array("check"=>"require"),
		));
		
		$data['files'] = str_replace("__*@*__",',',$data['files']);
		$files = explode(',',$data['files']);
		if(!$files){
			show_json(LNG('explorer.error'),false);
		}
		$res = $this->modelSource->removeFromTag($files,$data['tagID']);
		if(!$res && count($files) == 1){ // 部分老数据处理; 文件夹统一去除结尾斜杠;
			$files[0] = rtrim($files[0],'/');
			$res = $this->modelSource->removeFromTag($files,$data['tagID']);
		}
		show_json(LNG('explorer.success'),true);
	}
	
	//添加文档到tag;
	public function filesAddToTag(){
		$data = Input::getArray(array(
			"tagID"	=> array("check"=>"int"),
			"files"	=> array("check"=>"require"),
		));
		$data['files'] = str_replace("__*@*__",',',$data['files']);
		$files = explode(',',$data['files']);
		if(!$files){
			show_json(LNG('explorer.error'),false);
		}
		foreach ($files as $file) {
			$res = $this->fileAddTag($file,$data['tagID']);
		}
		show_json(LNG('explorer.success'),true);
	}
	
	// 标签包含内容数量上限控制;
	private function fileAddTag($file,$tagID){
		$count = $this->modelSource->where(array('tagID'=>$tagID))->count();
		if( $count > $GLOBALS['config']['systemOption']['tagContainMax'] ){
			show_json(LNG("common.numberLimit"),false);
		}
		Action('explorer.listSafe')->authCheckAllow($file);
		return $this->modelSource->addToTag($file,$tagID);
	}
	
}
tagGroup.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/tagGroup.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 部门公共标签处理
 * get					//部门标签获取 [参数]:groupID
 * set					//部门标签设置 [参数]:groupID,value
 * filesRemoveFromTag 	//部门文件移除公共标签 [参数]:groupID,files,tagID
 * filesAddToTag 		//部门文件添加公共标签 [参数]:groupID,files,tagID
 * 
 * -------------- 
 * tagAppendItem()		//部门文档,追加公共标签信息;
 * listSource()			//根据部门标签筛选内容
 */
class explorerTagGroup extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  = Model('GroupTag');
		$this->checkAuth();
	}

	/*
	获取设置标签; 前后顺序即为排序关系
	{group:[{name:xx,icon:xx}...]  ,list:[{id:xx,name:xx},...],   ...};// 标签分组,标签列表;
	*/
	public function get(){
		$data = Input::getArray(array(
			"groupID"	=> array("check"=>"int"),
		));
		$tagList = $this->groupTag($data['groupID']);
		show_json($tagList);
	}
	
	// 标签数据写入; (groupTag:mysql-text字段; json 64k/100 =6千个) 
	// 标签设置检测: id不重复;名称不重复; 分组名称不同;
	public function set(){
		$data = Input::getArray(array(
			'diff'		=> array("check"=>"json"),
			"groupID"	=> array("check"=>"int"),
		));
		
		$dataLike = array(
			'group' => array(array('_idKey_'=>'id','_autoID_'=>"number")),
			'list'  => array(array('_idKey_'=>'id','_autoID_'=>"number")),
		);
		$listData = $this->groupTag($data['groupID']);
		$data['value']  = kodDiff::diffApply($listData,$data['diff'],$dataLike);
		
		// 新建分组时处理;
		foreach($data['value']['list'] as &$item){
			if($item['_groupAddTemp']){
				$group = array_find_by_field($data['value']['group'],'_groupAddTemp',$item['_groupAddTemp']);
				if($group && $group['id']){$item['group'] = $group['id'];}
			}
			unset($item['_groupAddTemp']);
		};unset($item);
		foreach ($data['value']['group'] as &$item) {
			unset($item['_groupAddTemp']);
		};unset($item);
		// pr($data['value'],$listData,$data['diff']);exit;
		
		if(!$this->tagSetCheck($data['value'],$data['groupID'])){
			show_json(LNG("common.invalidParam"),false);
		}
		Model('SystemLog')->addLog('explorer.tagGroup.set', $data);//操作日志记录;
		$this->model->set($data['groupID'],$data['value']);
		show_json($this->groupTag($data['groupID']),true);
	}
	
	// 标签设置检测: id不重复;名称不重复; 分组名称不同;  
	// 标签id有删除:前端强提醒, 清空该标签关联到的文档(tagID对应的文档-removeByTag);
	private function tagSetCheck($listData,$groupID){
		if(!is_array($listData) || !is_array($listData['list'])) return false;
		$idList = array();$tagNameList = array();
		foreach($listData['list'] as $tag){
			if(!$tag['id']){return false;}
			if(in_array($tag['id'],$idList)){return false;}
			if(in_array($tag['name'],$tagNameList)){return false;}
			$idList[] = $tag['id'];$tagNameList[] = $tag['name'];
		}
		
		// 标签有删除时; 则解除删除标签对文档的关联;
		$beforeList = $this->groupTag($groupID);
		foreach($beforeList['list'] as $tag){
			if(in_array($tag['id'],$idList)) continue;
			$this->model->removeByTag($groupID,$tag['id']);
		}
		return true;
	}
	
	// 部门子内容,公共标签追加; 根部门--追加 groupTagList
	public function tagAppendItem(&$pathInfo){
		if(!isset($pathInfo['targetType']) || isset($pathInfo['shareID'])) return $pathInfo;
		if($pathInfo['targetType'] != 'group') return $pathInfo;
		
		$groupID = $pathInfo['targetID'];
		$groupTagInfo = $this->groupTagGet($groupID);
		
		// 是否为部门管理员,继承当前文档权限( 下层权限小于上层权限, 使用下层权限)
		if(is_array($pathInfo['auth']) && !AuthModel::authCheckRoot($pathInfo['auth']['auth'])){
			$groupTagInfo['isGroupRoot'] = false;
		}
		$pathInfo['sourceInfo']['isGroupRoot']   = $groupTagInfo['isGroupRoot'];	// 是否为该部门管理员
		$pathInfo['sourceInfo']['isGroupHasTag'] = $groupTagInfo['isGroupHasTag'];	// 是否有公共标签

		// 部门根目录
		if($pathInfo['parentID'] == '0'){
			$pathInfo['sourceInfo']['groupTagList'] = $groupTagInfo;
		}
				
		// 部门子目录;关联标签; sourceInfo['groupTagInfo'];
		$groupSource = $this->sourceTagList($groupID);
		if(isset($groupSource[$pathInfo['sourceID']])){
			$tags = $groupSource[$pathInfo['sourceID']];
			$pathInfo['sourceInfo']['groupTagInfo'] = $this->getTags($groupID,$tags);
		}
		return $pathInfo;
	}
	
	private function groupTagGet($groupID){
		static $list = array();
		if(!isset($list[$groupID])){
			$groupTag = $this->groupTag($groupID);
			$groupTag['isGroupRoot']   = Action('filter.userGroup')->allowChangeGroup($groupID);
			$groupTag['isGroupHasTag'] = isset($groupTag['list']) && count($groupTag['list']) > 0;
			$list[$groupID] = $groupTag;
		}
		return $list[$groupID];
	}
	private function groupTag($groupID){
		$listData = $this->model->get($groupID);//pr($listData);exit;
		if($listData['_hasDiff']){return $listData;}
		
		// 旧版数据处理兼容; 分组id,标签id自适应处理;
		$listData['_hasDiff'] = true;unset($listData['idMax']);
		if(!$listData['list'] ){$listData['list']  = array();}
		if(!$listData['group']){$listData['group'] = array();}
		kodDiff::arrayAutoID($listData['list']);
		kodDiff::arrayAutoID($listData['group']);
		foreach ($listData['list'] as $i => &$tag){
			$tag['group'] = _get($listData['group'][$tag['group']],'id','1');
		};unset($tag);
		$this->model->set($groupID,$listData);
		return $listData;
	}
	
	// 该部门下文档公共标签关联数据;
	private function sourceTagList($groupID){
		static $list = array();
		if(!isset($list[$groupID])){
			$arr = $this->model->listData($groupID);
			$list[$groupID] = array_to_keyvalue_group($arr,'path','tagID');
		}
		return $list[$groupID];
	}
	private function getTags($groupID,$tags){
		static $list = array();
		if(!isset($list[$groupID])){
			$arr = $this->groupTag($groupID);
			$arr['listTag'] = array_to_keyvalue($arr['list'],'id');
			$list[$groupID] = $arr;
		}

		$result  = array();
		$tagList = $list[$groupID];
		if(!$tags || !$tagList || !$tagList['list']) return $result;
		foreach($tags as $tagID){
			$tagInfo = $tagList['listTag'][$tagID.''];
			if(!$tagInfo) continue;
			$tagInfo['groupInfo'] = array_find_by_field($tagList['group'],'id',$tagInfo['group']);
			unset($tagInfo['group']);
			$result[] = $tagInfo;
		}
		return $result;
	}
	
	// 根据$id获取部门筛选tag的文档列表;
	public function listSource($groupID,$tags){
		$tags   	= $tags[0] ? $tags : false;
		$groupInfo 	= Model('Group')->getInfo($groupID);
		$result 	= Model("GroupTag")->listSource($groupID,$tags);
		$tagList 	= $this->getTags($groupID,$tags);
		$tagName 	= $tags ? implode(',',array_to_keyvalue($tagList,'','name')) : '-';

		$tagInfo = array('name'=>$tagName,'pathDesc'=>LNG('explorer.groupTag.pathDesc'));
		$tagInfo['pathAddress'] = array(
			array("name"=> $groupInfo['name'],"path"=>KodIO::make($groupInfo['sourceInfo']['sourceID'])),
			array("name"=> LNG('common.tag').': '.$tagInfo['name'],"path"=>$this->in['path']),
		);
		if(!$result){$result = array("fileList"=>array(),'folderList'=>array());}

		$tagInfo['sourceRoot'] = "groupPath";
		$result['currentFieldAdd'] = $tagInfo;
		$result['groupTagList'] = Model("GroupTag")->get($groupID);
		return $result;
	}

	
	/**
	 * 权限检测;
	 * 部门公共标签编辑: 仅部门管理员有权限
	 * 文档标签设置: 对文档有编辑权限
	 */
	private function checkAuth(){
		if(strtolower(MOD.'.'.ST) != 'explorer.taggroup') return;
		$ACTION = strtolower(ACT);
		$checkSourceAuth = array('filesRemoveFromTag','filesAddToTag');
		
		// 文档权限检测;
		$groupID = $this->in['groupID'];
		foreach($checkSourceAuth as $action){
			if($ACTION != strtolower($action)) continue;
			$files = explode(',',$this->in['files']);
			foreach($files as $file){
				$path = KodIO::make($file);
				if(!Action('explorer.auth')->fileCanWrite($path)){
					show_json(LNG('explorer.noPermissionAction'),false);
				}
				$pathInfo = IO::info($path);
				if($pathInfo['targetType'] != 'group' || $pathInfo['targetID'] != $groupID){
					show_json(LNG('common.notExists'),false);
				}
			}
			return;
		}
	}


	//======== tag关联资源管理 =========	
	//将文档从某个tag中移除
	public function filesRemoveFromTag(){
		$data = Input::getArray(array(
			"tagID"		=> array("check"=>"int"),
			"files"		=> array("check"=>"require"),
			"groupID"	=> array("check"=>"int"),
		));
		$files = explode(',',$data['files']);
		if(!$files){show_json(LNG('explorer.error'),false);}
		
		$res = $this->model->removeFromTag($data['groupID'],$files,$data['tagID']);
		show_json($res? LNG('explorer.success'): LNG('explorer.error'),!!$res);
	}
	
	//添加文档到tag;
	public function filesAddToTag(){
		$data = Input::getArray(array(
			"tagID"	=> array("check"=>"int"),
			"files"	=> array("check"=>"require"),
			"groupID"	=> array("check"=>"int"),
		));
		$files = explode(',',$data['files']);
		if(!$files){show_json(LNG('explorer.error'),false);}
		
		foreach ($files as $file) {
			$res = $this->model->addToTag($data['groupID'],$file,$data['tagID']);
		}
		show_json(LNG('explorer.success'),true);
	}

	// 前后顺序即为排序关系; 标签组包含子标签
	public function testData(){
		$listData = array(
			'group' => array(
				array('name'=>'用户规模','icon'=>'ri-user-line'),
				array('name'=>'客户分类','icon'=>'ri-profile-line'),
				array('name'=>'所属行业','icon'=>'ri-building-line'),
				array('name'=>'项目状态','icon'=>'ri-money-cny-circle-line'),
			),
			'list'	=> array(
				array('id'=>1,'name'=>'<50','group'=>'0'),
				array('id'=>2,'name'=>'50~100','group'=>'0'),
				array('id'=>3,'name'=>'100~300','group'=>'0'),
				array('id'=>4,'name'=>'300~500','group'=>'0'),
				array('id'=>5,'name'=>'500~1000','group'=>'0'),
				array('id'=>6,'name'=>'1000~3000','group'=>'0'),
				array('id'=>7,'name'=>'>3000','group'=>'0'),

				array('id'=>8,'name'=>'个人采购','group'=>'1'),
				array('id'=>9,'name'=>'企业采购','group'=>'1'),
				array('id'=>10,'name'=>'第三方','group'=>'1'),
				array('id'=>11,'name'=>'合作伙伴','group'=>'1'),
				
				array('id'=>12,'name'=>'金融保险','group'=>'2'),
				array('id'=>13,'name'=>'IT互联网','group'=>'2'),
				array('id'=>14,'name'=>'能源行业','group'=>'2'),
				array('id'=>15,'name'=>'学校教育','group'=>'2'),
				array('id'=>16,'name'=>'政府单位','group'=>'2'),
				array('id'=>17,'name'=>'房地产','group'=>'2'),
				
				array('id'=>18,'name'=>'跟进中','group'=>'3'),
				array('id'=>19,'name'=>'成单-已付款','group'=>'3'),
				array('id'=>20,'name'=>'未成单-已终止','group'=>'3'),
			)
		);
		return $listData;
	}
}
upload.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/upload.class.php'
View Content
<?php
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerUpload extends Controller{
	public function __construct(){
		parent::__construct();
		$this->model = Model("Source");
	}
	public function pathAllowReplace($path){
		$notAllow = array('\\', ':', '*', '?', '"', '<', '>', '|',"\r","\n");//不允许字符
		$db = $this->config['database'];// 文件文件夹名emoji是否支持处理;
		if(!isset($db['DB_CHARSET']) || $db['DB_CHARSET'] != 'utf8mb4'){
			$path = preg_replace_callback('/./u',function($match){return strlen($match[0]) >= 4 ? '-':$match[0];},$path);
		}
		return str_replace($notAllow,'_',$path);
	}
	
	
	// 通过上传内容获得上传临时文件;(插件;文件编辑保存)
	public function fileUploadTemp(){
		$this->in["chunkSize"] = '0';
		$this->in["size"] = '0';
		
		$uploader  = new Uploader();
		$localFile = $uploader->upload();
		$uploader->statusSet(false);
		return $localFile;
	}
	
	/**
	 * 上传,三个阶段
	 * checkMd5:上传前;秒传处理、前端上传处理
	 * uploadLinkSuccess: 前端上传完成处理;
	 * 其他: 正常通过后端上传上传到后端;
	 */
	public function fileUpload(){
		$this->authorizeCheck();
		$uploader = new Uploader();
		$savePath = $this->in['path'];
		if(!IO::exist($savePath)) show_json(LNG('explorer.upload.errorPath'),false);
		if( $this->in['fullPath']){//带文件夹的上传
			$fullPath = KodIO::clear($this->in['fullPath']);
			$fullPath = $this->pathAllowReplace($fullPath);
			$fullPath = get_path_father($fullPath);
			$savePath = IO::mkdir(rtrim($savePath,'/').'/'.$fullPath);
		}
		$repeat = Model('UserOption')->get('fileRepeat');
		$repeat = isset($this->in['fileRepeat']) ? $this->in['fileRepeat'] : $repeat;
		$repeat = isset($this->in['repeatType']) ? $this->in['repeatType'] : $repeat; // 向下兼容pc客户端
		
		// 上传前同名文件处理(默认覆盖; [覆盖,重命名,跳过])
		$uploader->fileName = $this->pathAllowReplace($uploader->fileName);
		if($repeat == REPEAT_RENAME){
			$uploader->fileName = IO::fileNameAuto($savePath,$uploader->fileName,$repeat);
			if(!$uploader->fileName){show_json('skiped',true);}
		}
		$savePath = rtrim($savePath,'/').'/'.$uploader->fileName;
		
		// 文件保存; 必须已经先存在;
		if($this->in['fileSave'] == '1'){
			$repeat = REPEAT_REPLACE;
			$info   = IO::info($this->in['path']);
			if(!$info){
				show_json(LNG("common.pathNotExists"),false);
			}
			
			$this->in['name'] = $info['name'];
			$uploader->fileName = $this->pathAllowReplace($info['name']);
			$parent = IO::pathFather($info['path']);
			if(!$parent){show_json(LNG("common.pathNotExists"),false);}
			$savePath = rtrim($parent,'/').'/'.$info['name'];// 重新构造路径 父目录+文件名;
		}
		
		// 第三方存储上传完成
		if( isset($this->in['uploadLinkSuccess']) ){
			$this->fileUploadByClient($savePath,$repeat);
		}
		if( isset($this->in['checkType']) ){
			$this->fileUploadCheckExist($uploader,$savePath,$repeat);
		}
		
		// 通过服务器上传;
		$localFile = $uploader->upload();
		$path = IO::upload($savePath,$localFile,true,$repeat);//本地到本地则用move的方式;
		$uploader->clearData();//清空上传临时文件;
		// pr($localFile,$path,$savePath,$uploader,$this->in);exit;
		if($path){
			show_json(LNG("explorer.upload.success"),true,$this->uploadInfo($path));
		}else{
			show_json(IO::getLastError(LNG("explorer.upload.error")),false);
		}
	}
	private function uploadInfo($path){
		$info = IO::info($path);
		// 记录文件本身最后修改时间;
		if($info && $this->in['modifyTime']){
			$modifyTime = abs(intval(substr($this->in['modifyTime'],0,10)));
			if($modifyTime > 1000 && $modifyTime < time()){
				IO::setModifyTime($path,$modifyTime);
			}
		}
		
		if($this->in['fileInfo'] != '1') return $path;
		$info = array_field_key($info,array("ext",'name','createTime','size','path','pathDisplay'));
		$info['downloadPath'] = Action('explorer.share')->link($path);
		return $info;
	}
	
	
	// 第三方上传获取凭证
	private function authorizeCheck(){
		if( !isset($this->in['authorize']) ) return;
		$inPath = $this->in['path'];
		if(substr(IO::getType($inPath), 0, 2) == 'db'){
			$ioFileDriver = KodIO::ioFileDriverGet($inPath);
			$path = '{io:'.$ioFileDriver['id'].'}/'.$inPath;
		}else{
			$pathBase = substr($inPath, 0, stripos($inPath, '/'));
			$path = (!$pathBase ? $inPath : $pathBase) . '/' . $inPath;
		}
		$paramMore = $this->getParamMore();
		$result = IO::uploadMultiAuth($path, $paramMore);
		show_json($result, true);
	}

	// 获取paramMore,兼容json和数组
	private function getParamMore(){
		if(!isset($this->in['paramMore'])) return array();
		$paramMore = $this->in['paramMore'];
		if (!is_array($paramMore)) {
			$paramMore = json_decode($paramMore, true);
			if (!$paramMore) return array();
		}
		// 兼容旧版 ext:?partNumber=1&uploadId=xxx
		if (!empty($paramMore['ext'])) {
			$query = parse_url_query($paramMore['ext']);
			if (is_array($query)) $paramMore = array_merge($paramMore, $query);	// partNumber, uploadId
			// unset($paramMore['ext']);
		}
		return $paramMore;
	}

	//秒传及断点续传处理
	private function fileUploadCheckExist($uploader,$savePath,$repeat){
		$size = $this->in['size'];
		$isSource   = false;
		$hashSimple = isset($this->in['checkHashSimple']) ? $this->in['checkHashSimple']:false;
		$hashMd5    = isset($this->in['checkHashMd5']) ? $this->in['checkHashMd5']:false;
		if(substr(IO::getType($savePath), 0, 2) == 'db' && $hashSimple ){
			$isSource = true;
			$file = Model("File")->findByHash($hashSimple,$hashMd5);
		}else{
			$file = array('hashSimple' => null, 'hashMd5' => null);	// 非绑定数据库存储不检查秒传
		}
		
		if(!$file['hashMd5']){$file['hashSimple'] = null;}
		$checkChunkArray = array();
		if($hashSimple){$checkChunkArray = $uploader->checkChunk();} // 断点续传保持处理;
		
		$ioFileDriver = KodIO::ioFileDriverGet($savePath);
		$infoData = array(
			"checkChunkArray"	=> $checkChunkArray,
			"checkFileHash"		=> array(
				"hashSimple"=>$file['hashSimple'],
				"hashMd5"	=>$file['hashMd5']
			),
			"uploadLinkInfo"	=> IO::uploadLink($savePath, $size),//前端上传信息获取;
			"uploadToKod"		=> $isSource,
			"uploadChunkSize"	=> $this->config['settings']['upload']['chunkSize'],
			"kodDriverType"		=> $ioFileDriver['driver'],
		);
		$linkInfo = &$infoData['uploadLinkInfo'];
		// trace_log(['fileUploadCheckExist',$savePath,$linkInfo,$ioFileDriver['name'].':'.$ioFileDriver['name']]);
		if(isset($linkInfo['host'])){ // 前端上传时,自适应处理(避免http,https混合时浏览器拦截问题; )
		    $linkInfo['host'] = str_replace("http://",'//',$linkInfo['host']);
			// $linkInfo['host'] = str_replace("https://",'//',$linkInfo['host']);	// 存储只限https访问时去掉会有异常
		}
		$this->checkAllowUploadWeb($infoData);
		
		// 保留参数部分; kod挂载kod的webdav前端上传;
		if($this->in['addUploadParam']){$infoData['addUploadParam'] = $this->in['addUploadParam'];} // server;
		if($linkInfo['webdavUploadTo']){$infoData = $linkInfo;}	// webdav client 首次检测中转访问;
		
		if( $this->in['checkType'] == 'matchMd5' && 
			!empty($this->in['checkHashMd5']) && 
			!empty($file['hashMd5']) && 
			$this->in['checkHashMd5'] == $file['hashMd5']
		){
			$path = IO::uploadFileByID($savePath,$file['fileID'],$repeat);
			$uploader->clearData();//清空上传临时文件;
			show_json(LNG('explorer.upload.secPassSuccess'),true,$this->uploadInfo($path));
		}else{
			show_json(LNG('explorer.success'),true,$infoData);
		}
	}
	
	// 检测, 是否允许前端对象存储直传(腾讯cos+Android浏览器form分片上传时,)
	private function checkAllowUploadWeb(&$infoData){
		if(!$infoData['uploadLinkInfo']){return;}
		// if(stristr($_SERVER['HTTP_USER_AGENT'],'android')){$infoData['uploadLinkInfo'] = false;}
	}

	/**
	 * 前端上传,完成后记录并处理;
	 * 
	 * $key是完整路径,type为DB(即为默认io)时,$savePath={source:x}/$key,
	 * 获取默认io判断:{io:n}/$key
	 * 否则,$savePath={io:x}/$key,直接判断
	 */
	private function fileUploadByClient($savePath,$repeat){
		$paramMore  = $this->getParamMore();
		$remotePath = $this->parsePath(KodIO::ioFileDriverGet($savePath),$this->in['key']);

		// 耗时操作;
		if(!IO::isFile($remotePath)){
			show_json(LNG("explorer.upload.error"), false);
		}
		$path = IO::addFileByRemote($savePath, $remotePath,$paramMore,$this->in['name'],$repeat);
		show_json(LNG("explorer.upload.success"),true,$this->uploadInfo($path));
	}
	private function parsePath($driver,$path){
		$bucket		= isset($driver['config']['bucket']) ? $driver['config']['bucket'].'/':'';
		$pathBase	= trim($driver['config']['basePath'], '/');
	    $pathPre	= $bucket.$pathBase;
	    if(substr($path,0,strlen($pathPre)) == $pathPre){
	        $path = substr($path,strlen($pathPre));
	    }else if(!empty($pathBase) && substr($path,0,strlen($pathBase)) == $pathBase){
			$path = substr($path,strlen($pathBase));
		}
	    $remotePath = '{io:'.$driver['id'].'}/'.trim($path, '/');
	    return $remotePath;
	}
	
	// 远程下载
	public function serverDownload() {
		if(!$this->in['uuid']){
			$this->in['uuid'] = md5($this->in['url']);
		}
		$uuid = 'download_'.$this->in['uuid'];
		$this->serverDownloadCheck($uuid);
		$url 	= $this->in['url'];
		$savePath = rtrim($this->in['path'],'/').'/';
		$header = url_header($url);
		if (!$header){
			show_json(LNG('download_error_exists'),false);
		}

		$filename = _get($this->in,'name',$header['name']);
		$filename = unzip_filter_ext($filename);
		$tempFile = TEMP_FILES.md5($uuid);
		mk_dir(TEMP_FILES);
		Session::set($uuid,array(
			'supportRange'	=> $header['supportRange'],
			'length'		=> $header['length'],
			'path'			=> $tempFile,
			'name'			=> $filename,
		));
		$this->serverDownloadHashCheck($url,$header,$savePath,$filename,$uuid);
		$result = Downloader::start($url,$tempFile);
		if($result['code']){
			$outPath = IO::copy($tempFile,$savePath,REPEAT_RENAME,$filename);
			show_json(LNG('explorer.downloaded'),true,IO::info($outPath));
		}else{
			show_json($result['data'],false);
		}
	}

	/**
	 * 远程下载秒传处理;
	 * 小于10M的文件不处理;
	 */
	private function serverDownloadHashCheck($url,$header,$savePath,$filename,$uuid){
		return;// 暂时关闭该特性;
		if($header['length'] < 10 * 1024*1024) return false;
		$driver = new PathDriverUrl();
		$fileHash = $driver->hashSimple($url,$header); // 50个请求;8s左右;
		$file = Model("File")->findByHash($fileHash);
		if(!$fileHash || !$file) return;
		
		$tempFile = $file['path'];
		Session::remove($uuid);
		$outPath = IO::copy($tempFile,$savePath,REPEAT_RENAME);
		$fileName= IO::fileNameAuto($savePath,$filename,REPEAT_RENAME);
		$outPath = IO::rename($outPath,$fileName);
		show_json(LNG('explorer.upload.secPassSuccess'),true,IO::info($outPath));
	}
	
	
	private function serverDownloadCheck($uuid){
		$data = Session::get($uuid);
		if ($this->in['type'] == 'percent') {
			if (!$data) show_json('uuid error',false);
			$result = array(
				'supportRange' => $data['supportRange'],
				'uuid'      => $this->in['uuid'],
				'length'    => intval($data['length']),
				'name'		=> $data['name'],
				'size'      => intval(@filesize($data['path'].'.downloading')),
				'time'      => mtime()
			);
			show_json($result);
		}else if($this->in['type'] == 'remove'){//取消下载;文件被删掉则自动停止
			if($data){
				IO::remove($data['path'].'.downloading');
				IO::remove($data['path'].'.download.cfg');
				Session::remove($uuid);
			}
			show_json('');
		}
	}
}
userShare.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/userShare.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerUserShare extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  = Model('Share');
	}
		
	// 通过文档获取分享;没有则返回false;
	public function get(){
		$path = Input::get('path','require');
		$pathParse = KodIO::parse($path);$shareInfo = false;
		if($pathParse['type'] == KodIO::KOD_SHARE_ITEM){
			show_json($this->getShareInfo($pathParse['id']));
		}
		
		// 物理路径,io路径;
		if( $pathParse['type'] == KodIO::KOD_IO || !$pathParse['type'] ){
			$shareInfo = $this->model->getInfoBySourcePath(rtrim($path,'/'));
		}else{
			$sourceID = KodIO::sourceID($path);
			$shareInfo = $this->model->getInfoByPath($sourceID);
		}
		if($shareInfo && $shareInfo['userID'] != KodUser::id()){
			show_json(LNG('explorer.noPermissionAction'),false);
		}
		$shareInfo = Action('explorer.shareOut')->sendShareSiteAppend($shareInfo);
		show_json($shareInfo);
	}
	
	// 内部协作分享,分享对象角色权限为拥有者时,可以编辑该协作分享(成员及权限/过期时间)
	private function getShareInfo($shareID,$checkAuth = true){
		$shareInfo  = $this->model->getInfo($shareID);
		if(!$shareInfo){show_json(LNG('explorer.noPermissionAction'),false);}
		if($shareInfo['userID'] == KodUser::id()){return $shareInfo;}
		
		$sourceInfo = $this->sharePathInfo($shareID);
		if( !$sourceInfo || !$sourceInfo['auth'] || 
			!Model('Auth')->authCheckRoot($sourceInfo['auth']['authValue']) ){
			if($checkAuth){ // 编辑后不检测权限(与我协作-管理者,移除或降低自己的权限)
				return show_json(LNG('explorer.noPermissionAction'),false);
			}
		}
		$shareInfo['selfIsShareToUser'] = true;
		$shareInfo['sourceInfo'] = $sourceInfo;
		return $shareInfo;
	}
	
	// 分享信息处理;
	public function shareAppendItem(&$item){
		$shareInfo = _get($item,'shareInfo');
		if(!isset($item['sourceInfo'])){$item['sourceInfo'] = array();}
		if(isset($item['sourceInfo']['shareInfo']) ) return $item;
		
		static $shareList = false;
		if($shareList === false){
			$shareList = $this->model->listSimple();
			$shareList = array_to_keyvalue($shareList,'sourcePath');
		}
		
		$shareInfo = $shareInfo ? $shareInfo : _get($shareList,$item['path']);
		$shareInfo = $shareInfo ? $shareInfo : _get($shareList,rtrim($item['path'],'/'));
		$shareInfo = $shareInfo ? $shareInfo : _get($shareList,rtrim($item['path'],'/').'/');
		if(!$shareInfo) return $item;
		$item['sourceInfo']['shareInfo'] = Model("Source")->shareFilterShow($shareInfo);
		return $item;
	}

	/**
	 * 我的分享列表
	 * 点击进入对应文档目录;
	 * link/to
	 */
	public function myShare($type=''){
		$shareList = $this->model->listData($type);
		$result = array('fileList'=>array(),'folderList'=>array(),'pageInfo'=>$shareList['pageInfo']);
		$sourceArray = array_to_keyvalue($shareList['list'],'','sourceID');
		$sourceArray = array_unique($sourceArray);
		if($sourceArray){
			$where = array(
				'sourceID' => array('in',$sourceArray),
				'isDelete' => 0,
			);
			$sourceList  = Model('Source')->listSource($where);
			$sourceArray = array_merge($sourceList['folderList'],$sourceList['fileList']);
			$sourceArray = array_to_keyvalue($sourceArray,'sourceID');
		}
		$notExist = array();
		$sourceCountGroup = 0;$sourceCountIO = 0;
		foreach ($shareList['list'] as $shareItem) {
			$shareItem = Model("Source")->shareFilterShow($shareItem);
			if($shareItem['sourceID'] == '0'){// IO 对象存储等加速;
				$info = $this->sharePathInfoCheck($shareItem['sourcePath'],$shareItem);
			}else{
				$info = $sourceArray[$shareItem['sourceID']];
			}
			if(!$info){
				$notExist[] = $shareItem['shareID'];
				continue;
			}
			$info['sharePath'] = KodIO::makeShare($shareItem['shareID'],$info['sourceID']);
			$info['shareInfo'] = $shareItem;
			$key = $info['type'] == 'folder' ? 'folderList':'fileList';
			$this->shareTarget($info,$shareItem);
			
			// 协作内容不再有分享权限时处理; 其他人内容--隐藏; 自己的内容-突出显示;(前提:自己有内部协作或外链分享的对应角色权限);
			$shareType = $type == 'to' ? 'shareTo':'shareLink';
			$authType  = $shareType == 'shareTo' ? 'explorer.share':'explorer.shareLink';
			if( Action('user.authRole')->authCan($authType) &&
				!Action('explorer.authUser')->canShare($shareItem)){
				$this->removeShare($shareItem,$shareType);continue;
			}
			$result[$key][] = $info;
			if(!$info['sourceID']){$sourceCountIO++;}
			if($info['groupParentLevel']){$sourceCountGroup++;}
		}
		// if($notExist){$this->model->remove($notExist);}// 自动清除不存在的分享内容;
		
		// 对自己和部门内容进行分组;
		$groupShow = array(
			'selfData' => array(
				'type' 	=> 'selfData',
				'title' => LNG('explorer.pathGroup.shareSelf'),
				"filter"=> array('groupParentLevel'=>'_null_','sourceID'=>'')
			),
			'groupData' => array(
				'type'	=> 'groupData',
				'title'	=> LNG('explorer.pathGroup.shareGroup'),
				"filter"=> array('groupParentLevel'=>'','sourceID'=>'')
			),
			'ioData' => array(
				'type'	=> 'ioData',
				'title'	=> LNG('admin.storage.localStore').'/IO',
				"filter"=> array('sourceID'=>'_null_')
			),
		);
		$length = count($result['folderList']) + count($result['fileList']);
		if($sourceCountGroup == 0){unset($groupShow['groupData']);}
		if($sourceCountIO == 0){unset($groupShow['ioData']);}
		if($length <= 3 || count($groupShow) <= 1){$groupShow = array();}
		if($groupShow){$result['groupShow'] = array_values($groupShow);}
		return $result;
	}
	private function sharePathInfoCheck($path,$shareItem){
		$parse = KodIO::parse($path);
		if ($parse['driverType'] == 'io') {
			$driver = Model('Storage')->listData($parse['id']);
			if (!$driver) return false;

			$type = strtolower($driver['driver']);
			$typeList = $GLOBALS['config']['settings']['ioClassList'];
			$class = 'PathDriver'.(isset($typeList[$type]) ? $typeList[$type] : ucfirst($type));
			if( !class_exists($class) ) return false;
		}
		
		// 外部联合分享;
		if($shareItem['isLink'] == '0' && _get($shareItem,'options.site')){
			$path = '{shareItemOut:'.$shareItem['shareID'].'}/';			
		}
		
		try {
			$info = IO::info($path);
		} catch (Exception $e){$info = false;}
		return $info;
	}
	
	public function shareToMe($type=''){
		$allowTree = Model("SystemOption")->get('shareToMeAllowTree');
		$showType  = Model('UserOption')->get('shareToMeShowType');
		if($allowTree == '0'){$showType = 'list';}
		if( $showType == 'group' || strpos($type, 'group') === 0){
			return Action('explorer.userShareGroup')->get($type);
		}
		if( $showType == 'user'  || strpos($type, 'user') === 0 ){
			return Action('explorer.userShareUser')->get($type);
		}
		
		Model("share_to")->selectPageReset();// 确保被筛选后分页数据正常;
		$shareList = $this->model->listToMe(5000);
		Model("share_to")->selectPageRestore();
		$shareHide = Model('UserOption')->get('hideList','shareToMe');
		$shareHide = $shareHide ? json_decode($shareHide,true):array();
		foreach ($shareList['list'] as $key=>$shareItem){
			if(isset($shareHide[$shareItem['shareID'].''])){
				$shareItem['shareHide'] = 1;
			}else{ 
				$shareItem['shareHide'] = 0;
			}
			$shareList['list'][$key] = $shareItem;
			//$type:  '':显示内容; hide: 隐藏内容; all: 全部内容;
			if($type == '' && $shareItem['shareHide']){$shareList['list'][$key] = false;}
			if($type == 'hide' && !$shareItem['shareHide']){$shareList['list'][$key] = false;}
		}
		$result = $this->shareToMeListMake($shareList['list']);
		$result['currentFieldAdd'] = array("pathDesc"=>'['.LNG('admin.setting.shareToMeList').'],'.LNG('explorer.pathDesc.shareToMe'));
		return $result;
	}
	public function shareToMeListMake($shareList){
		$result = array('fileList'=>array(),'folderList'=>array());
		$sourceArray = array_to_keyvalue($shareList,'','sourceID');
		$sourceArray = array_unique($sourceArray);
		if($sourceArray){
			$where = array(
				'sourceID' => array('in',$sourceArray),
				'isDelete' => 0,
			);
			
			Model('Source')->selectPageReset();
			$sourceList  = Model('Source')->listSource($where);
			Model('Source')->selectPageRestore();
			$sourceArray = array_merge($sourceList['folderList'],$sourceList['fileList']);
			$sourceArray = array_to_keyvalue($sourceArray,'sourceID');
		}
		$notExist = array();
		foreach($shareList as $shareItem) {
			if(!$shareItem) continue;
			$timeout = intval(_get($shareItem,'options.shareToTimeout',0));
			if($timeout > 0 && $timeout < time()) continue;// 过期内容;
			if($shareItem['sourceID'] == '0'){// 物理路径,io路径;
				$info = $this->sharePathInfoCheck($shareItem['sourcePath'],$shareItem);
			}else{
				$info = $sourceArray[$shareItem['sourceID']];
			}
			if(!$info){
				$notExist[] = $shareItem['shareID'];
				continue;
			}
			$info = $this->_shareItemeParse($info,$shareItem);
			if(!$info) continue;
			if(_get($info,'shareUser.userID') == '0'){$info['isFromSystem'] = '1';} // 系统分享标记;
			$key  = $info['type'] == 'folder' ? 'folderList':'fileList';
			if(isset($shareItem['shareHide'])){$info['shareHide'] = $shareItem['shareHide'];}
			$result[$key][] = $info;
		}
		
		// 对自己和部门内容进行分组;
		$groupShow = array(
			'userData' => array(
				'type' 	=> 'userData',
				'title' => LNG('explorer.toolbar.shareToMe'),
				"filter"=> array('shareID'=>'','isFromSystem'=>'_null_','isShareOut'=>'_null_')
			),
			'systemData' => array(
				'type'	=> 'systemData',
				'title'	=> LNG('explorer.share.shareSystem'),'desc' => LNG('explorer.share.shareSystemDesc'),
				"filter"=> array('isFromSystem'=>'','isShareOut'=>'_null_')
			),
			'outerData' => array(
				'type'	=> 'outerData',
				'title'	=> LNG('explorer.shareOut.titlePath'),"desc"=>LNG('explorer.shareOut.titlePathDesc'),
				"filter"=> array('isShareOut'=>'')
			),
		);
		$length = count($result['folderList']) + count($result['fileList']);
		if($length <= 3){$groupShow = array();}
		if($groupShow){$result['groupShow'] = array_values($groupShow);}
		// if($notExist){$this->model->remove($notExist);} // 自动清除不存在的分享内容;
		return $result;
	}
	
	
	public function shareDisplay(){
		$data = Input::getArray(array(
			"shareArr"	=> array("check"=>"json","default"=>array()),
			"isHide"	=> array("check"=>"bool","default"=>'1'),
		));
		
		$shareHide = Model('UserOption')->get('hideList','shareToMe');
		$shareHide = $shareHide ? json_decode($shareHide,true):array();
		foreach ($data['shareArr'] as $shareID) {
			$shareID = $shareID.'';
			if($data['isHide'] == '1'){
				$shareHide[$shareID] = '1';
			}else{
				unset($shareHide[$shareID]);
			}
		}
		Model('UserOption')->set('hideList',json_encode($shareHide),'shareToMe');
		show_json(LNG('explorer.success'),true);
	}
	
	// 自己退出当前分享 (所有协作成员都为用户,且成员包含自己时--可退出;)
	public function shareExit(){
		$data = Input::getArray(array(
			"shareArr"	=> array("check"=>"json","default"=>array()),
		));
		$isRoot = KodUser::isRoot();
		$errorArr = array();
		foreach($data['shareArr'] as $shareID){
			$shareInfo  = $this->model->getInfo($shareID);
			if( !$shareInfo || _get($shareInfo,'isShareTo') != '1' ||
				!is_array($shareInfo['authList'])){
				$errorArr[] = LNG('explorer.share.notExist');
				continue;
			}
			
			$allUser = true;$hasSelf = false;$selfIndex = 0;
			$authListNew = array();
			foreach($shareInfo['authList'] as $i => $item){
				$isUser = $item['targetType'] == SourceModel::TYPE_USER;
				if(!$isUser){$allUser = false;}
				if($isUser && $item['targetID'] == KodUser::id()){
					$hasSelf = true;$selfIndex = $i;
				}else{
					$authListNew[] = $item;
				}
			}
			// if(!$allUser){$errorArr[] = "share target all must be [user]";continue;}
			if(!$hasSelf){$errorArr[] = "share target not include you";continue;}
			
			$shareOptions = is_array($shareInfo['options']) ? $shareInfo['options']:array();
			if($shareOptions['site']){
				$shareTarget = _get($shareOptions,'shareTarget',array());
				$shareTargetNew = array();$userSecret = '';
				foreach($shareTarget as $i => $userItem){
					if($i == $selfIndex){$userSecret = $userItem['secret'];}
					if($i != $selfIndex){$shareTargetNew[] = $userItem;}
				}
				$res = Action("explorer.shareOut")->shareUserExit($shareOptions['site'],$shareOptions['shareID'],$userSecret);
				if(is_array($res) && $res['info'] == 'kodbox'){
					if(!$res['code']){$errorArr[] = $res['data'];}
				}
				$shareOptions['shareTarget'] = $shareTargetNew;
			}
			if(!$authListNew && $shareInfo['isLink'] == '0'){
				$this->model->remove($shareID);
			}else{
				$this->model->shareEdit($shareID,array('isShareTo'=>'1','authTo'=>$authListNew,'options'=>$shareOptions));
			}
		}
		$code = count($errorArr) > 0 ? false:true;
		$msg  = $code ? LNG('explorer.success') : implode(',',$errorArr);
		show_json($msg,$code);
	}	
	
	// 分享内容属性; 默认$sourceID为空则分享本身属性; 指定则文件夹字内容属性;
	public function sharePathInfo($shareID,$sourceID=false,$withChildren=false){
		$shareInfo = $this->model->getInfo($shareID);
		if($shareInfo['sourceID'] == '0'){
			$truePath = KodIO::clear($shareInfo['sourcePath'].$sourceID);
			// $sourceInfo = IO::info($truePath);
			$sourceInfo = array('path'=>$truePath);
		}else{
			$sourceID = $sourceID ? $sourceID : $shareInfo['sourceID'];
			if(!$withChildren){
				$sourceInfo = Model('Source')->pathInfo($sourceID);
			}else{
				$sourceInfo = Model('Source')->pathInfoMore($sourceID);
			}
		}
		// pr($sourceID,$truePath,$sourceInfo,$shareInfo);exit;
		
		if(!$this->shareIncludeCheck($shareInfo,$sourceInfo)) return false;
		$sourceInfo = $this->_shareItemeParse($sourceInfo,$shareInfo);
		return $sourceInfo;
	}
	
	// 检测附带文档是否归属于该分享;
	private function shareIncludeCheck($shareInfo,$sourceInfo){
		// pr_trace($shareInfo,$sourceInfo);exit;
		if(!$shareInfo || !$sourceInfo) return false;
		
		// 物理路径,io路径;
		if($shareInfo['sourceID'] == '0'){
			$sharePath = KodIO::clear($shareInfo['sourcePath']);
			$thisPath  = KodIO::clear($sourceInfo['path']);
			if(substr($thisPath,0,strlen($sharePath)) != $sharePath) return false;
			return true;
		}
		
		$shareSource = $shareInfo['sourceInfo'];
		// 分享目标为文件,追加子内容必须是自己;
		if( $shareSource['type'] == 'file' &&
			$shareSource['sourceID'] != $sourceInfo['sourceID']){
			return false; 
		}
		if( $shareSource['type'] == 'folder' &&
			strpos($sourceInfo['parentLevel'],$shareSource['parentLevel']) !== 0 ){
			return false; 
		}
		return true;
	}
	
	public function sharePathList($parseInfo){
		$shareID  	= $parseInfo['id'];
		$param    	= explode('/',trim($parseInfo['param'],'/'));
		$shareInfo	= $this->model->getInfo($shareID);
		
		// 物理路径,io路径;
		if($shareInfo['sourceID'] == '0'){
			$truePath = KodIO::clear($shareInfo['sourcePath'].$parseInfo['param']);
			$sourceInfo = $this->sharePathInfoCheck($truePath,$shareInfo);
			if(!$sourceInfo) return false;
			$list = IO::listPath($truePath);
		}else{
			$sourceID   = $param[0] ? $param[0] : $shareInfo['sourceID'];
			$sourceInfo = Model('Source')->pathInfo($sourceID);
			if(!$this->shareIncludeCheck($shareInfo,$sourceInfo)) return false;
			$list = Model('Source')->listSource(array('parentID' => $sourceID));
		}
	
		foreach ($list as $key => &$keyList) {
			if($key != 'folderList' && $key != 'fileList' ) continue;
			foreach ($keyList as &$source) {
				$source = $this->_shareItemeParse($source,$shareInfo);
			};unset($source);
		};unset($keyList);

		$list['current'] = $this->_shareItemeParse($sourceInfo,$shareInfo);
		// pr($parseInfo,$truePath,$sourceInfo,$shareInfo,$list);exit;
		return $list;
	}

	/**
	 * 处理source到分享列表
	 * 去除无关字段;处理parentLevel,pathDisplay
	 */
	public function _shareItemeParse($source,$share){
		if(!$source || !is_array($source)){return false;}
		$share = Model("Source")->shareFilterShow($share);
		$sourceBefore = $source;
		$user = Model('User')->getInfoSimpleOuter($share['userID']);
		$source['auth']	= Model("SourceAuth")->authMake($share['authList']);//覆盖原来文档权限;每次进行计算
		$source['shareCreateTime'] 	= $share['createTime'];
		$source['shareModifyTime'] 	= $share['modifyTime'];
		$source['shareID']  = $share['shareID'];
		if($source['isShareOut']){return $source;}
		
		$sourceRoot = isset($share['sourceInfo']) ? $share['sourceInfo'] : $source;
		$source['shareUser'] = $user;
		// 物理路径,io路径;
		$pathAdd = $source['sourceID'];
		if($share['sourceID'] == '0'){
			$sharePath = KodIO::clear($share['sourcePath']);
			$thisPath  = KodIO::clear($source['path']);
			$pathAdd   = substr($thisPath,strlen($sharePath));
			if(substr($thisPath,0,strlen($sharePath)) != $sharePath) return false;

			// 子目录不再追加;
			if($pathAdd){unset($source['shareInfo']);}
		}
		$source['path'] = KodIO::makeShare($share['shareID'],$pathAdd);
		$source['path'] = KodIO::clear($source['path']);
		
		if($source['auth'] && $share['sourceID'] != '0'){
			$listData = array($source);
			Model('Source')->_listAppendAuthSecret($listData);
			$source = $listData[0];
			if($share['userID'] == USER_ID){
				$source['auth'] = $share['sourceInfo']['auth'];
			}
		}

		// 分享者名字;
		$userName = $user['nickName'] ? $user['nickName']:$user['name'];
		$displayUser = '['.$userName.']'.LNG('common.share').'-'.$sourceRoot['name'];
		if(!$user || $user['userID'] == '0'){$displayUser = $sourceRoot['name'];}

		if($share['userID'] == USER_ID){
			$displayUser = $sourceRoot['name'];
			$shareInfoAdd = Model("Source")->shareFilterShow($share);
			$source['sourceInfo']['selfShareInfo'] = array_merge($sourceRoot,$shareInfoAdd);
			if(!$pathAdd){$source['sourceInfo']['shareInfo'] = $share;}
		}
		if($sourceRoot['targetType'] == 'group'){
			$source['sharePathFrom'] = $sourceRoot['pathDisplay'];
		}else{
			$source['sharePathFrom'] = LNG('explorer.toolbar.rootPath').'('.$userName.')';
		}
		$source['parentLevel'] = ',0,'.substr($source['parentLevel'],strlen($sourceRoot['parentLevel']));
		$sourceNameArr = explode('/',trim($source['pathDisplay'],'/'));
		$sourceRootNameArr = explode('/',trim($sourceRoot['pathDisplay'],'/')); 

		// 通过目录层级截取;避免类似于根目录name不一致的情况;
		$source['pathDisplay'] = $displayUser.'/'.implode('/',array_slice($sourceNameArr,count($sourceRootNameArr)));
		//$source['pathDisplay'] = $displayUser.'/'.substr($source['pathDisplay'],strlen($sourceRoot['pathDisplay']));
		if($share['sourceID'] == '0'){
			$source['parentLevel'] = '';
			$source['pathDisplay'] = $displayUser.'/'.$pathAdd;
		}
		
		$source['pathDisplay'] = KodIO::clear($source['pathDisplay']);
		if($source['type'] == 'folder'){
			$source['pathDisplay'] = rtrim($source['pathDisplay'],'/').'/';
		}

		// 读写权限;
		if($source['auth']){// 读写权限同时受: 来源权限+设置权限;
			$isWriteable = true;$isReadable = true;
			if($share['sourceID'] == '0'){ // 物理路径协作分享,保留原来权限;
				$isWriteable = array_key_exists('isWriteable',$source) ?  $source['isWriteable'] : true;
				$isReadable  = array_key_exists('isReadable',$source)  ?  $source['isReadable']  : true;
			}
			// 物理路径分享,自己访问自己分享的内容时权限处理;
			if($share['sourceID'] == '0' && $share['userID'] == USER_ID){
				$source['auth']['authValue'] = AuthModel::authAll();
			}
			$source['isWriteable'] = $isWriteable && AuthModel::authCheckEdit($source['auth']['authValue']);
			$source['isReadable']  = $isReadable  && AuthModel::authCheckView($source['auth']['authValue']);
		}
		if(isset($source['sourceInfo']['tagInfo'])){
			unset($source['sourceInfo']['tagInfo']);
		}
		$this->shareTarget($source,$share);
		
		// 协作内容不再有分享权限时处理; 其他人内容--隐藏; 自己的内容-突出显示;
		if(!Action('explorer.authUser')->canShare($share)){return false;}
		// pr($source,$sourceBefore,$share);exit;
		return $source;
	}
	
	private function shareTarget(&$source,$share){
		$isRoot = false;
		if($source['sourceID'] && $source['sourceID'] == $share['sourceID']){$isRoot = true;}
		if($share['sourcePath'] && count(explode('/',trim($source['path'],'/'))) == 1){$isRoot = true;}
		if(!$source['shareID']){$isRoot = true;}
		$source['sourceInfo']['shareIsRoot'] = $isRoot;
		if(!$isRoot) return; // 子文件夹不显示协作成员;
		$source['shareTarget'] = $this->shareTargetMake($share['authList']);
	}

	public function shareTargetMake($authList){
		$userArr = array();$groupArr = array();
		foreach($authList as $item){
			$auth = Model("SourceAuth")->authInfo($item);
			if($item['targetType'] == SourceModel::TYPE_GROUP){
				$item = Model("Group")->getInfoSimple($item['targetID']);
				$item['auth'] = $auth;$groupArr[] = $item;
			}else{
				$item = Model("User")->getInfoSimpleOuter($item['targetID']);
				$item['auth'] = $auth;$userArr[] = $item;
			}
		}
		return array_merge($groupArr,$userArr);
	}
	
	/**
	 * 添加分享;
	 */
	public function add(){
		$data = $this->_getParam('sourceID');
		$pathParse = KodIO::parse($data['path']);
		$this->checkRoleAuth($data['isLink'] ? 'shareLink':'shareTo');
		
		// 物理路径,io路径;
		$data['sourcePath'] = KodIO::clear($data['path']);
		if( $pathParse['type'] == KodIO::KOD_IO || !$pathParse['type'] ){
			$result = $this->model->shareAdd('0',$data);
		}else{
			$sourceID = KodIO::sourceID($data['path']);
			$pathInfo = Model('Source')->pathInfo($sourceID);
			$this->checkSetAuthAllow($data['authTo'],$pathInfo);
			$result = $this->model->shareAdd($sourceID,$data);
		}
		
		if(!$result) show_json(LNG('explorer.error'),false);
		$shareInfo = $this->model->getInfo($result);
		$shareInfo = Action('explorer.shareOut')->sendShareSiteAppend($shareInfo);
		show_json($shareInfo,true);
	}

	/**
	 * 编辑分享
	 */
	public function edit(){
		$data = $this->_getParam('shareID');
		$this->checkRoleAuth($data['isLink'] == '1' ? 'shareLink':'shareTo');
		$shareInfo = $this->getShareInfo($data['shareID']);
		$this->checkSetAuthAllow($data['authTo'],$shareInfo['sourceInfo']);
		$result = $this->model->shareEdit($data['shareID'],$data);
		
		// 编辑后不检测权限(与我协作-管理者,移除或降低自己的权限)
		if(!$result){show_json("[URL] ".LNG('explorer.pathExists'),false);}
		show_json($this->getShareInfo($data['shareID'],false));
	}
	
	// 协作分享: 可以设置的权限,必须小于等于自己在当前文档的权限;
	public function checkSetAuthAllow($authTo,$pathInfo){
		if(!$authTo || !$pathInfo || !$pathInfo['auth']) return;
		$selfAuth = intval($pathInfo['auth']['authInfo']['auth']);
		foreach ($authTo as $authItem){
			$authInfo = Model("Auth")->listData($authItem['authID']);
			$authBoth = $selfAuth | intval($authInfo['auth']);
			if($authBoth == $selfAuth) continue;
			show_json(LNG('admin.auth.errorAdmin'),false);
		}
	}
	
	private function checkRoleAuth($shareType){
		if(KodUser::isRoot()){return;}
		$canShareTo   = Action('user.authRole')->authCan('explorer.share');
		$canShareLink = Action('user.authRole')->authCan('explorer.shareLink');
		if($shareType == 'shareTo' && !$canShareTo){
			show_json(LNG('explorer.noPermissionAction'),false,1004);
		}
		if($shareType == 'shareLink' && !$canShareLink){
			show_json(LNG('explorer.noPermissionAction'),false,1004);
		}
	}
	
	/**
	 * 添加/编辑分享;
	 * shareType: 
	 * 		0: 暂未指定分享
	 * 		1: 内部指定用户分享
	 * 		2: 外链分享
	 * 		3: 内部指定、外链分享同时包含
	 * 
	 * 外链分享; title,password,timeTo,options
	 * authTo: [
	 * 		{"targetType":"1","targetID":"23","authID":"1"},
	 * 		{"targetType":"2","targetID":"3","authDefine":"512"}
	 * ]
	 * param: title,password,timeTo,options
	 */
	private function _getParam($key='shareID'){
		$keys = array(
			"isLink"	=> array("check"=>"bool",	"default"=>0),
			"isShareTo"	=> array("check"=>"bool",	"default"=>0),
			"title"		=> array("check"=>"require","default"=>''),
			"password"	=> array("default"=>''),//密码设置为空处理;
			"timeTo"	=> array("check"=>"require","default"=>0),
			"options"	=> array("check"=>"json",	"default"=>array()),
			"authTo"	=> array("check"=>"json", 	"default"=>array()),
		);
		//修改,默认值为null不修改;
		if($key == 'shareID'){
			$keys['shareID'] = array("check"=>"int");
			foreach ($keys as $key => &$value) {
				$value['default'] = null;
			};unset($value);
		}else{//添加时,目录值
			$keys['path'] = array("check"=>"require");
		}
		$data = Input::getArray($keys);
		
		// 外链分享检测;
		if($data['isLink'] == '1'){
			$options = Model('SystemOption')->get();
			if($options['shareLinkAllow'] == '0'){
				show_json(LNG('admin.setting.shareLinkAllowTips'),false);
			}
			if($options['shareLinkPasswordAllowEmpty'] == '0' && !$data['password']){
				show_json(LNG('user.pwdNotNull'),false);
			}
			if($options['shareLinkAllowGuest'] == '0'){
				$data["options"]['onlyLogin'] = '1';
			}
			// 外链分享,分享者角色没有上传权限时, 不允许开启允许上传;
			if(!Action('user.authRole')->authCan('explorer.upload')){
				$data["options"]['canUpload'] = '0';
				$data["options"]['canEditSave'] = '0';
				if($options['shareLinkAllowEdit'] == '0'){$data["options"]['canEditSave'] = '0';}
			}
			
			if(_get($this->in,'hash')){
				$data['shareHash'] = trim(rawurldecode($this->in['hash']));
				$data['shareHash'] = substr(preg_replace('/[^\w\-\._]/', '_', $data['shareHash']),0,45);
			}
		}
		return $data;
	}

	/**
	 * 批量取消分享;
	 * 如果制定了分享类型: 则不直接删除数据; 
	 */
	public function del() {
		$list  = Input::get('dataArr','json');
		$shareType = _get($this->in,'type','');
		// 批量删除指定内部协作分享, or外链分享;
		foreach ($list as $shareID) {
			$shareInfo = $this->model->getInfo($shareID);
			if(!$shareInfo || $shareInfo['userID'] != KodUser::id()){continue;}
			if(!$shareType){
				$res = $this->model->remove(array($shareID));
				continue;
			}
			$res = $this->removeShare($shareInfo,$shareType);
		}
		$msg  = !!$res ? LNG('explorer.success'): LNG('explorer.error');
		show_json($msg,!!$res);
	}
	
	public function removeShare($shareInfo,$shareType){
		$this->checkRoleAuth($shareType == 'shareTo' ? 'shareTo':'shareLink');
		if($shareType == 'shareTo'){
			$data = array('isShareTo'=>0,'authTo'=>array(),'options'=>$shareInfo['options']);
			if(isset($data['options']['shareToTimeout'])){unset($data['options']['shareToTimeout']);}
		}else{
			$data = array('isLink'=>0,'options'=>$shareInfo['options']);
			if(is_array($data['options'])){
				unset($data['options']['onlyLogin']);
				unset($data['options']['pageType']);
				unset($data['options']['shareOut']);
			}
		}
		// 都为空时则删除数据, 再次分享shareID更新;
		if( $data['isLink'] == 0 && $shareInfo['isShareTo'] == 0 ||
			$data['isShareTo'] == 0 && $shareInfo['isLink'] == 0 ){
			return $this->model->remove(array($shareInfo['shareID']));
		}
		return $this->model->shareEdit($shareInfo['shareID'],$data);
	}
}
userShareGroup.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/userShareGroup.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt

按组织架构对与我协助内容进行归类
1. 子部门: 该部门子部门,下级部门有分享或下级成员有分享时显示
2. 部门成员的个人空间分享; 包含个人空间文件,其他物理路径,外部部门空间分享;
3. 该部门空间协作分享: 该部门空间内容;
----
4. 外部协作分享: 不在自己组织架构下的外部用户的协作分享;
*/

class explorerUserShareGroup extends Controller{
	private $model;
	private $userGroupRootShow;
	private $shareListData;
	function __construct(){
		parent::__construct();
		$this->model  = Model('Share');
	}
	public function get($id){
		$this->userGroupRootShow  = Action('filter.userGroup')->userGroupRootShow();
		$this->shareListData  = $this->shareListDataMake();
		if(!$id || $id == 'group'){
			return $this->listRoot();
		}

		$userPre  = 'group-u';//group-u101-23; userID-parentGroup
		$groupPre = 'group-g';//group-g101;
		if(substr($id,0,strlen($groupPre)) == $groupPre){
			$groupID = substr($id,strlen($groupPre));
			return $this->listByGroup($groupID);
		}
		if(substr($id,0,strlen($userPre)) == $userPre){
			$userGroup = explode('-',substr($id,strlen($userPre)));
			return $this->listByUser($userGroup[0],$userGroup[1]);
		}
	}
	
	private function listRoot(){
		$rootCount = count($this->userGroupRootShow);
		if($rootCount == 0) return false;
		$outUserList  = $this->listUserOuter();
		if($rootCount == 1){
			return $this->listRootSingle($this->userGroupRootShow[0],$outUserList);
		}

		$childrenGroup = Model("Group")->listByID($this->userGroupRootShow);
		$childrenGroup = array_sort_by($childrenGroup,'sort',false);
		$groupList = array();
		foreach($childrenGroup as $groupInfo){
			$childList   = $this->listByGroup($groupInfo['groupID']);
			if(!$childList || (!$childList['groupList'] && !$childList['folderList'])){continue;}
			$groupList[] = $this->makeItemGroup($groupInfo);
		}
		if(!$groupList) return false;
		if(count($groupList) == 1){
			return $this->listRootSingle($groupList[0]['groupID'],$outUserList);
		}
		
		$result = $this->listByGroupType(array('groupList'=>$groupList));
		$result['groupList'] = array_merge($result['groupList'],$outUserList);
		return $result;
	}
	private function listRootSingle($groupID,$outUserList){
		$result = $this->listByGroup($groupID);
		$desc   = '['.LNG('admin.setting.shareToMeGroup').'],'.LNG('explorer.pathDesc.shareToMeGroup');
		if($result){$result['currentFieldAdd'] = array("pathDesc"=>$desc);}
		// 合并外部分享信息数据;
		
		if(!$result['groupList']){$result['groupList'] = array();}
		$result['groupList'] = array_merge($result['groupList'],$outUserList);
		return $result;
	}
	
	// 当前部门有分享内容的子部门, 当前部门分享内容, 当前部门有分享内容的用户;
	private function listByGroup($groupID){
		$groupID = intval($groupID);
		if(!$groupID) return false;
		$groupInfoCurrent = Model("Group")->getInfo($groupID);
		if(!$groupInfoCurrent) return false;

		$listData   = $this->shareListData;
		$groupArray = array_keys($listData['group']);
		$userArray  = array_keys($listData['user']);		
		$goupList = Model("Group")->listByID($groupArray);
		$userList = Model("User")->listByID($userArray);
		
		$childrenUser  = array();
		$childrenGroup = array();
		foreach($goupList as $groupInfo){
			$groupLevel = $groupInfo['parentLevel'].$groupInfo['groupID'].',';//层级
			$childrenID = $this->groupChildrenMake($groupID,$groupLevel);
			if($childrenID){$childrenGroup[] = $childrenID;}
		}
		foreach ($userList as $userInfo){
			foreach ($userInfo['groupInfo'] as $groupInfo){
				$groupLevel = $groupInfo['parentLevel'].$groupInfo['groupID'].',';//层级
				$childrenID = $this->groupChildrenMake($groupID,$groupLevel);
				if($childrenID){$childrenGroup[] = $childrenID;}
				if($groupInfo['groupID'] == $groupID){$childrenUser[] = $userInfo;}
			}
		}
		$childrenGroup = Model("Group")->listByID(array_unique($childrenGroup));
		$childrenGroup = array_sort_by($childrenGroup,'sort',false);
		
		$groupList = array();
		foreach($childrenGroup as $groupInfo){$groupList[] = $this->makeItemGroup($groupInfo);}
		foreach($childrenUser  as $userInfo){$groupList[] = $this->makeItemUser($userInfo,$groupInfoCurrent);}

		$result = array('groupList'=>$groupList,'folderList'=>$listData['group'][$groupID]);
		$result['currentFieldAdd'] = $this->makeItemGroup($groupInfoCurrent);
		$result = $this->listByGroupType($result);
		return $result;
	}
	
	private function listByGroupType($result){
		if(!$result['folderList']){$result['folderList'] = array();}
		if(!$result['fileList']){$result['fileList'] = array();}
		$result['groupShow'] = array(
			array(
				'type' 	=> 'childGroup',
				'title' => LNG("explorer.pathGroup.shareGroup"),
				'desc' 	=> LNG("explorer.pathGroup.shareGroupDesc"),
				"filter"=> array('groupID'=>'')
			),
			array(
				'type'	=> 'childUser',
				'title'	=> LNG("explorer.pathGroup.shareUser"),
				'desc'	=> LNG("explorer.pathGroup.shareUserDesc"),
				"filter"=> array('userID'=>'','shareFrom'=>'!=outer')
			),
			array(
				'type'	=> 'childUserOuter',
				'title'	=> LNG('explorer.pathGroup.shareUserOuter'),
				"desc"	=> LNG("explorer.pathGroup.shareUserOuterDesc"),
				"filter"=> array('shareFrom'=>'outer')
			),
			array(
				'type'	=> 'childContent',
				'title'	=> LNG("explorer.pathGroup.shareContent"),
				"filter"=> array('shareID'=>'')
			),
		);
		if(count($result['groupList']) == 0){unset($result['groupShow']);}
		// filter 支持多个key-value; 全匹配才算匹配; 
		// value为*则检测是否有该key; 为字符串则检测相等; value为数组则代表可选值集合
		// show_json([$groupID,$groupList,$result]);exit;
		return $result;
	}
	
	// $groupID是否为$parentLevel的上层部门; 如果是则返回$groupID下一层部门id;
	private function groupChildrenMake($groupID,$checkLevel){
		$level = explode(',',trim($checkLevel,','));
		$level = array_remove_value($level,'0');
		$index = array_search($groupID,$level);
		if($index === false || $index == count($level) - 1) return false;
		return $level[$index + 1];
	}
	public function groupAllowShow($groupID,$parentLevel=false){
		$allow  = false;
		if(in_array($groupID,$this->userGroupRootShow)) return true;
		if(!$parentLevel){
			$groupInfo = Model('Group')->getInfo($groupID);
			$parentLevel = $groupInfo['parentLevel'].$groupInfo['groupID'].',';
		}
		foreach($this->userGroupRootShow as $group){
			if($this->groupChildrenMake($group,$parentLevel)){$allow = true;break;}
		}
		return $allow;
	}
	
	private function makeItemGroup($groupInfo){
		$result = array(
			"groupID"		=> $groupInfo['groupID'],
			"name" 			=> $groupInfo["name"],
			"type" 			=> "folder",
			"path" 			=> "{shareToMe:group-g".$groupInfo['groupID']."}/",
			"icon"			=> "root-groupPath",
			"pathDesc"		=> LNG('explorer.pathDesc.shareToMeGroupGroup'),
		);
		$parentGroup = Model("Group")->getInfo($groupInfo['parentID']);
		return $this->makeAddress($result,$parentGroup);
	}
	private function makeItemUser($userInfo,$parentGroup=false){
		$defaultThumb = STATIC_PATH.'images/common/default-avata.png';
		$defaultThumb = 'root-user-avatar';// $userInfo["avatar"] = false;
		$parentGroup  = $parentGroup ? $parentGroup:array('groupID'=>'0','parentLevel'=>'');
		$result = array(
			"userID"		=> $userInfo['userID'],
			"name" 			=> $userInfo['nickName'] ? $userInfo['nickName']:$userInfo['name'],
			"type" 			=> "folder",
			"path" 			=> "{shareToMe:group-u".$userInfo['userID']."-".$parentGroup['groupID']."}/",
			"icon"			=> $userInfo["avatar"] ? $userInfo["avatar"]:$defaultThumb,//fileThumb,icon
			"iconClassName"	=> 'user-avatar',	
		);
		$result['pathDescAdd'][] = array(
			"title"		=> LNG("common.desc"),
			"content"	=> LNG('explorer.pathGroup.shareUserDesc')
		);
		$userInfo = Model('User')->getInfo($userInfo['userID']);
		$groupArr = array_to_keyvalue($userInfo['groupInfo'],'','groupName');
		if($groupArr){
			$result['pathDescAdd'][] = array("title"=>LNG("admin.member.group"),"content"=>implode(',',$groupArr));
		}
		return $this->makeAddress($result,$parentGroup);
	}
	
	private function makeAddress($itemInfo,$parentGroup){
		$address = array(array("name"=> LNG('explorer.toolbar.shareToMe'),"path"=>'{shareToMe}'));
		// 从$this->uuserGroupRootShow的某一个,开始到$groupInfo所在部门
		$level = $parentGroup ? $parentGroup['parentLevel'].$parentGroup['groupID'].',':'';
		$level = explode(',',trim($level,','));
		$level = array_remove_value($level,'0');
		
		$fromAdd = count($this->userGroupRootShow) == 1 ? 1: 0;//只有一个根部门,则忽略根部门;
		$index   = false;
		if($level){
			foreach($this->userGroupRootShow as $groupID){
				$index = array_search($groupID,$level);
				if($index !== false){break;}
			}
		}
		if($index !== false){
			$nameArray = explode('/',trim($parentGroup['groupPath']));
			for ($i=$index+$fromAdd; $i < count($level); $i++) { 
				$address[] = array("name"=> $nameArray[$i],"path"=>"{shareToMe:group-g".$level[$i]."}/");
			}
		}
		$address[] = array("name"=> $itemInfo["name"],"path"=>$itemInfo['path']);
		$itemInfo['pathAddress'] = $address;
		return $itemInfo;
	}

	private function listByUser($userID,$parentGroup){
		$userShareList = $this->shareListData['user'][$userID];
		if(!$userShareList) return false;
		
		$userInfo = Model('User')->getInfoSimpleOuter($userID);
		$result = Action('explorer.userShare')->shareToMeListMake($userShareList);
		$groupInfo = Model('Group')->getInfo($parentGroup);
		$result['currentFieldAdd'] = $this->makeItemUser($userInfo,$groupInfo);
		// unset($result['currentFieldAdd']['icon']);
		return $result;
	}
	
	// 对内容归类整理: 所属用户,所属部门,
	private function shareListDataMake(){
		$shareList = $this->model->listToMe(5000);
		$sourceArray = array_to_keyvalue($shareList['list'],'','sourceID');
		$sourceArray = array_unique($sourceArray);
		if($sourceArray){
			$where = array('sourceID' => array('in',$sourceArray),'isDelete' => 0,);
			$sourceList  = Model('Source')->listSource($where);
			$sourceArray = array_merge($sourceList['folderList'],$sourceList['fileList']);
			$sourceArray = array_to_keyvalue($sourceArray,'sourceID');
		}
		$userList = array();$groupList = array();
		foreach ($shareList['list'] as $shareItem){
			$timeout = intval(_get($shareItem,'options.shareToTimeout',0));
			if($timeout > 0 && $timeout < time()) continue;// 过期内容;
			if($shareItem['sourceID'] == '0'){// 物理路径,io路径;
				// $info = IO::info($shareItem['sourcePath']);
				$info = $shareItem;//性能优化, 拉取用户内容时才考虑;
			}else{
				$info = $sourceArray[$shareItem['sourceID']];
			}

			if(!$info) continue;
			$groupNotAllowShare = false;// 部门内容分享,该部门对自己不可见则归类到该用户的分享;
			if($info['targetType'] == 'group' && !$this->groupAllowShow($info['targetID'])){
				$groupNotAllowShare = true;
			}
			if($groupNotAllowShare || $shareItem['sourceID'] == '0' || $info['targetType'] == 'user'){// 物理路径,io路径;
				$userID = $shareItem['userID'];
				if(!isset($userList[$userID])){$userList[$userID] = array();}
				$userList[$userID][] = $shareItem;//性能优化;
			}else{
				$info = Action('explorer.userShare')->_shareItemeParse($info,$shareItem);
				if(!$info) continue;
				$groupID = $info['targetID'];
				if(!isset($groupList[$groupID])){$groupList[$groupID] = array();}
				$groupList[$groupID][] = $info;
			}
		}
		return array('user'=>$userList,'group'=>$groupList);
	}
	
	// 筛选出外部分享内容; 部门空间内容-部门不在该自己所在组织架构内; 个人空间分享--个人不在自己所在组织架构内;
	// 按人进行归类;
	private function listUserOuter(){
		$userList = array();
		foreach($this->shareListData['user'] as $userID=>$userShare){
			$userInfo  = Model('User')->getInfo($userID);
			$userAllow = false;
			if(!$userInfo){$userInfo = Model('User')->getInfoSimpleOuter($userID);}
			foreach ($userInfo['groupInfo'] as $groupInfo){
				$groupLevel = $groupInfo['parentLevel'].$groupInfo['groupID'].',';//层级
				if($this->groupAllowShow($groupInfo['groupID'],$groupLevel)){$userAllow = true;break;}
			}
			if($userAllow) continue;
			
			$userItem   = $this->makeItemUser($userInfo);
			$userItem['shareFrom'] = 'outer';
			$userList[] = $userItem;
		}
		return $userList;
	}
}
userShareTarget.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/userShareTarget.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

/**
 * 最近分享目标获取;
 * 
 * 最近分享/权限设置出的用户及部门,按次数排序,总共10个,部门在前用户在后;
 * 数据缓存; 新查询数据合并之前缓存数据;(避免取消分享后没有最近使用的情况)
 */
class explorerUserShareTarget extends Controller{
	function __construct(){
		parent::__construct();
	}
	
	public function save(){
		$data = Input::getArray(array(
			"name"		=> array("check"=>"require"),
			"authTo" 	=> array("check"=>"require","default"=>''),
		));

		//编辑保存
		$saveData = $this->dataValue('saveData');
		if($this->in['beforeName']){
			unset($saveData[$this->in['beforeName']]);
		}
		$data['modifyTime'] = time();
		$saveData[$data['name']] = $data;

		// authTo为空则代表删除
		if(!$data['authTo']){
			unset($saveData[$data['name']]);
		}
								
		$this->dataValue('saveData',$saveData);
		$result = $this->get(10);
		show_json($result,true);
	}
	
	public function get($maxNumber){
		$maxNumber = 10; // 最多条数
		$list = $this->targetMake();
		foreach ($list as $targetType => $targetList){
			$list[$targetType] = array();
			foreach ($targetList as $info){
				if($targetType == 'group'){
					$targetInfo = Model("Group")->getInfo($info['id']);
				}else{
					$targetInfo = Model("User")->getInfoSimpleOuter($info['id']);
					if($targetInfo['userID'] == '0' || $targetInfo['userID'] == '-1'){
						$targetInfo = false;
					}
				}
				if(!$targetInfo) continue;
				$list[$targetType][] = $targetInfo;
			}
		}
		
		if( count($list['user']) + count($list['group']) > $maxNumber ){
			$list['user']  = array_slice($list['user'], 0,$maxNumber / 2);
			$list['group'] = array_slice($list['group'],0,$maxNumber / 2);
		}
		
		
		$saveData = $this->dataValue('saveData');
		foreach ($saveData as $key=>$value){
			$value['nodeAddClass'] = 'node-share-item-store';
			$value['icon'] = '<i class="font-icon ri-team-fill"></i>';
			$saveData[$key] = $value;
		}
		$saveData = array_values($saveData);
		$result = array_merge($saveData,$list['group'],$list['user']);
		return $result;
	}
	
	private function dataValue($key,$value=false){
		if($value === false){
			// Model("UserOption")->set($key,null,'shareTarget');
			$theValue = Model("UserOption")->get($key,'shareTarget',1);
			$theValue = is_array($theValue) ? $theValue : array();
		}else{
			Model("UserOption")->set($key,$value,'shareTarget');
		}
		return $theValue;
	}
	
	
	/**
	 * 新查询的数据合并之前数据;
	 * 
	 * 实时中id存在旧数据中不存在,    则设置id的count为实时数据的count
	 * 实时中id存在旧数据中已经存在,  则使用count更大的作为当前id的count;
	 * 旧数据中有,实时中id不存在,     检测对象是否存在,存在则id的count重置为1,不存在则从缓存中移除;
	 */
	private function targetMake(){
		$listNow = $this->targetSelect();$listNowData = $listNow;
		$listBefore = $this->dataValue('cacheData');
		foreach ($listNow as $targetType => $targetList){
			foreach ($targetList as $id=>$info){
				$beforeItem = $listBefore[$targetType][$id];
				if($beforeItem){
					$info['count'] = max($info['count'],$beforeItem['count']);
					$listNow[$targetType][$id] = $info;
				}
			}
		}
		
		// 缓存中有,新查询不存在
		foreach ($listBefore as $targetType => $targetList){
			foreach ($targetList as $id=>$info){
				if(!$listNow[$targetType][$id]){
					if($targetType == 'group'){
						$targetInfo = Model("Group")->getInfo($info['id']);
					}else{
						$targetInfo = Model("User")->getInfoSimpleOuter($info['id']);
					}
					if(!$targetInfo) continue;
					$info['count'] = 1;
					$listNow[$targetType][$id] = $info;
				}
			}
			$listNow[$targetType] = array_sort_by($listNow[$targetType] ,'count',true);
			$listNow[$targetType] = array_to_keyvalue($listNow[$targetType] ,'id');
		}

		if($listNow != $listBefore){
			$this->dataValue('cacheData',$listNow);
		}
		// pr($listNow == $listBefore,$listNowData,$listBefore,$listNow);exit;
		return $listNow;
	}
	
	private function targetSelect(){
		$shareList = Model('Share')->listSimple();
		$shareList = array_filter_by_field($shareList,'isShareTo','1');
		$shareIdList = array_to_keyvalue($shareList,'','shareID');
		if(!$shareIdList) return array('group'=>array(),'user'=>array());
		
		$where 	= array('shareID' => array('in',$shareIdList));
		$toList = Model("share_to")->where($where)->select();
		return array(
			'group' => $this->targetSort($toList,SourceModel::TYPE_GROUP),
			'user' 	=> $this->targetSort($toList,SourceModel::TYPE_USER)
		);
	}
	private function targetSort($shareList,$type){
		$list 	= array_filter_by_field($shareList,'targetType',$type.'');
		$list 	= array_to_keyvalue_group($list,'targetID','targetID');
		foreach ($list as $key => $value) {
			$list[$key] = array('id'=>$key,'count'=>count($value));
		}
		$list = array_sort_by($list,'count',true);
		return array_to_keyvalue($list,'id');
	}
}
userShareUser.class.php
wget 'https://sme10.lists2.roe3.org/kodbox/app/controller/explorer/userShareUser.class.php'
View Content
<?php 
/*
* @link http://kodcloud.com/
* @author warlee | e-mail:kodcloud@qq.com
* @copyright warlee 2014.(Shanghai)Co.,Ltd
* @license http://kodcloud.com/tools/license/license.txt
*/

class explorerUserShareUser extends Controller{
	private $model;
	function __construct(){
		parent::__construct();
		$this->model  = Model('Share');
	}
	public function get($id){
		if(!$id || $id == 'user') return $this->listRoot();
		return $this->listByUser(substr($id,strlen('user-')));
	}

	private function listRoot(){
		$shareList = $this->model->listToMe(5000);
		$userArray = array_to_keyvalue($shareList['list'],'','userID');
		$userArray = array_unique($userArray);

		$folderList= array();
		foreach($userArray as $userID ){
			$folderList[] = $this->makeItemUser(Model('User')->getInfoSimpleOuter($userID));
		}
		$result = array('fileList'=>array(),'folderList'=>$folderList);//,"disableSort"=>true
		$result['currentFieldAdd'] = array("pathDesc"=>'['.LNG('admin.setting.shareToMeUser').'],'.LNG('explorer.pathDesc.shareToMeUser'));
		return $result;
	}
	private function listByUser($userID){
		$shareList = $this->model->listToMe(5000);
		$listData  = array();
		foreach ($shareList['list'] as $shareItem){
			if($shareItem['userID'] == $userID){$listData[] = $shareItem;}
		}
		$result = Action('explorer.userShare')->shareToMeListMake($listData);
		$result['currentFieldAdd'] = $this->makeItemUser(Model('User')->getInfoSimpleOuter($userID));
		return $result;
	}
	
	private function makeItemUser($userInfo){
		$defaultThumb = STATIC_PATH.'images/common/default-avata.png';
		$defaultThumb = 'root-user-avatar';// $userInfo["avatar"] = false;
		$result = array(
			"name" 			=> $userInfo['nickName'] ? $userInfo['nickName']:$userInfo['name'],
			"type" 			=> "folder",
			"path" 			=> "{shareToMe:user-".$userInfo['userID']."}/",
			"icon"			=> $userInfo["avatar"] ? $userInfo["avatar"]:$defaultThumb,//fileThumb,icon
			"iconClassName"	=> 'user-avatar',		
		);
		$result['pathDescAdd'][] = array(
			"title"		=> LNG("common.desc"),
			"content"	=> LNG('explorer.pathDesc.shareToMeUserItem')
		);
		$userInfo = Model('User')->getInfo($userInfo['userID']);
		$groupArr = array_to_keyvalue($userInfo['groupInfo'],'','groupName');
		if($groupArr){
			$result['pathDescAdd'][] = array("title"=>LNG("admin.member.group"),"content"=>implode(',',$groupArr));
		}
		return $this->makeAddress($result);
	}
	private function makeAddress($itemInfo){
		$address = array(array("name"=> LNG('explorer.toolbar.shareToMe'),"path"=>'{shareToMe}'));
		$address[] = array("name"=> $itemInfo["name"],"path"=>$itemInfo['path']);
		$itemInfo['pathAddress'] = $address;
		return $itemInfo;
	}
}