* @package WB * @subpackage content */ /** * Load required class */ WBClass::load('WBContent' , 'WBDatasource' , 'WBVFS_File'); /** * Content component: Manage VFS * * @version 1.6.0 * @package WB * @subpackage content */ class WBContent_VFS extends WBContent { /** * my parameter list * @var array */ protected $config = array( 'process' => 'show', 'dir' => 0, 'user' => 0, 'requiredgroup' => 'vfs', 'excludedir' => '', 'translation' => 0, 'mimemajor' => '', 'clause' => array(), 'order' => '' ); /** * file object * @var WBVFS_File */ protected $file; /** * explorer object * @var WBVFS_Explorer */ protected $expl; /** * chunk class * @var WBVFS_ChunkUpload */ protected $chunk; /** * References * @var WBDatasource_Reference */ private $ref; /** * @var WBDatasource_Table */ private $table; /** * run * * run component * * @return array parameter list */ public function run() { $this->ref = WBClass::create('WBDatasource_Reference'); $this->ref->setNamespace('wb')->setTag('vfsfile'); $config = $this->config; foreach ($config as $k => &$v) { if (is_array($v)) { $v = ''; } } // display media file if ($this->config['process'] == 'display') { $this->tmpl->addGLobalVars($config, 'config_'); $this->displayVFSFile(); return $this->config; } if (!$this->isUserInGroup($this->config['requiredgroup'])) { $this->tmpl->addGLobalVars($config, 'config_'); $this->loadTemplates('anon'); $fid = 17; return $this->config; } $this->startVFS(); $dir = $this->config['dir']; if (empty($dir) || $dir === '0') { $dir = 0; } else { // verify dir WBClass::load('WBException_Datasource'); try { $node = $this->expl->getDir($dir); } catch (WBException_Datasource $e) { if ($e->getCode() != 'WBDatasource_Tree.1') { throw $e; } $dir = 0; } } $this->config['dir'] = $dir; $cwd = array(); $files = null; switch ($this->config['process']) { case 'chunkinit': $this->chunkInit(); break; case 'chunkupload': $this->chunkUpload(); break; case 'chunkimport': $this->chunkImport(); break; case 'upload': $this->processForm('upload'); return $this->config; break; case 'mkdir': $this->processForm('mkdir'); $this->loadTemplates('show'); break; case 'rmdir': if ($this->req->get('force', 'no') != 'yes') { $dirs = $this->expl->lsDir($dir); $cwd['dir_count'] = count($dirs); $files = $this->file->listDir($dir); $cwd['file_count'] = count($files); $this->loadTemplates('rmdir'); break; } $parent = $this->expl->getParentDir($dir); $this->expl->rmDir($dir); $dir = $parent['id']; $this->config['dir'] = $dir; $this->loadTemplates('show'); break; case 'exec': $id = $this->req->get('id'); /** @var WBVFS_File */ $file = $this->file->loadByObscureId($id); if (!$file->isOK()) { $this->loadTemplates('show'); break; } $dir = $file->getDirId(); $cmd = strtolower($this->req->get('cmd', null)); $arg = $this->req->get('cmdarg', array()); if ($cmd == 'save') { $save = array(); foreach (array('name', 'description') as $k) { if (isset($arg[$k])) { $save[$k] = strval(substr($arg[$k], 0, 240)) . ''; } } if (!empty($save)) { $file->save($save); } $this->loadTemplates('show'); break; } // execute command for mime type /** @var WBVFS_Mime */ $mimeHdl = WBClass::create('WBVFS_Mime_' . ucfirst($file->getMime())); $mimeHdl->setVirtualFile($file); $mimeHdl->execute($cmd, $arg); $file = $mimeHdl->getVirtualFile(); $this->loadTemplates('show'); break; case 'rm': $id = $this->req->get('id'); $file = $this->file->loadByObscureId($id); if (!$file->isOK()) { $this->loadTemplates('show'); break; } $dir = $file->getDirId(); $this->config['dir'] = $dir; if ($this->req->get('force', 'no') != 'yes') { $this->loadTemplates('rm'); $data = $file->get(); $data['uri'] = $file->getUri(); $this->tmpl->addGlobalVars($data); break; } $file->delete(); $this->loadTemplates('show'); break; case 'mvdir': case 'mvfile': $src = $this->req->get('src', array()); if (!is_array($src)) { $src = array($src); } $this->req->set('src', $src); $gotodes = intval($this->req->get('gotodes', 0)); if (0 < $gotodes) { $dir = $this->req->get('des', null); } else { if (0 < count($src)) { $dir = $src[0]; if ('mvfile' == $this->config['process']) { $file = $this->file->loadByObscureId($src[0]); $dir = $file->getDirId(); } else { $dir = $this->expl->getParentDirId($dir); } } } $this->processForm($this->config['process']); $this->loadTemplates('show'); break; case 'show'; $this->loadTemplates('show'); break; case 'find': $this->loadTemplates('find'); $files = $this->find($dir); break; case 'file'; $this->loadTemplates('file'); break; case 'dir'; $this->loadTemplates('dir'); break; } $cwd = array_merge($cwd, $this->expl->getDir($dir)); if (empty($cwd['id'])) { $cwd['id'] = "0"; } $cwd['parent'] = $this->expl->getParentDirId($dir); $cwd['path'] = $this->expl->getPath($dir, true); // list parent folders if ($this->tmpl->exists('parent_list_entry')) { $parents = $this->expl->getParent($dir); if (empty($parents)) { $parents = array(); } $this->tmpl->addRows('parent_list_entry', $parents); $cwd['parent_count'] = count($parents); } // list sub folders if ($this->tmpl->exists('dir_list_entry')) { $dirs = $this->getDirList($dir); $this->tmpl->addRows('dir_list_entry', $dirs); $cwd['dir_count'] = count($dirs); } // list files in folder if ($this->tmpl->exists('file_list_entry')) { $files = $this->getFileList($dir, $files); $this->tmpl->addRows('file_list_entry', $files); $cwd['file_count'] = count($files); } // display upload form if ($this->tmpl->exists('formupload')) { $form = $this->getForm('upload'); $this->renderForm($form); } // display mkdir form if ($this->tmpl->exists('formmkdir')) { $form = $this->getForm('mkdir'); $this->renderForm($form); } $config = $this->config; foreach ($config as $k => &$v) { if (is_array($v)) { $v = ''; } } $this->tmpl->addGlobalVars($config, 'config_'); $this->tmpl->addGlobalVars($cwd, 'cwd_'); return $this->config; } /** * Get list of subfolders * * @see WBVFS_Explorer::lsDir() * @param string $dir folder id * @return array $dirs */ protected function getDirList($dir) { return $this->expl->lsDir($dir); } /** * Get list of subfolders * * @see WBVFS_File::listDir() * @param string $dir folder id * @param null|array $files list of files already select or NULL * @return array $files */ protected function getFileList($dir, $files) { if (is_array($files)) { foreach ($files as &$f) { $this->injectReferences($f); } return $files; } $order = $this->getOrderOption(); $list = $this->file->listDir($dir, false, $order); foreach ($list as &$l) { $this->injectReferences($l); } return $list; } private function getOrderOption() { if (empty($this->config['order'])) { return; } $order = array(); $tmp = $this->config['order']; $asc = 1; if ('!' == $tmp[0]) { $tmp = substr($tmp, 1); $asc = 0; } $order[] = array( 'field' => $tmp, 'asc' => $asc ); return $order; } /** * Init chunked uploads * * Create one or more upload ids for chunked file upload */ private function chunkInit() { $data = json_decode($this->req->get("data", ""), true); $upload = array(); $this->startChunkUploader(); foreach ($data as $d) { try{ $d['uploadid'] = $this->chunk->create($d); } catch(WBException $e) { $d['uploadid'] = false; $d['error'] = $e->getCode(); } $upload[] = $d; } $this->sendJSONResponse($upload); } /** * Upload file's chunk * * Receive uploaded file and add it as chunk */ private function chunkUpload() { $this->startChunkUploader(); $this->chunk->loadById($this->req->get('uploadid')); $pos = $this->chunk->add(-1, file_get_contents($_FILES['file']['tmp_name'])); $data = array( 'uploadid' => $this->chunk->getId(), 'position' => $pos ); $this->sendJSONResponse($data); } /** * Import chunk * * Use request parameter: uploadid and name to create VFS File */ private function chunkImport() { $this->startChunkUploader(); $this->chunk->loadById($this->req->get('uploadid')); $this->file->setCurrentDir($this->config['dir']); try{ $this->chunk->import($this->file, $this->req->get('name')); $data = array( 'uploadid' => $this->chunk->getId(), 'vfsfile' => $this->file->getId() ); $this->chunk->rm(); } catch(WBException $e) { $data = array( 'uploadid' => $this->chunk->getId(), 'vfsfile' => false, 'error' => $e->getCode() ); } $this->sendJSONResponse($data); } /** * Find files * * load first matching file and select folder * * @see WBVFS_File::loadByName() * @param string $dir directory id * @return array */ private function find(&$diry) { $this->startVFS(); $query = $this->req->get('query', ''); $this->tmpl->addGlobalVar('find_query', $query); $order = $this->getOrderOption(); $list = $this->file->find($query, $order); return $list; } /** * display single media file * */ private function displayVFSFile() { $clause = array(); if (!empty($this->config['clause']) && is_array($this->config['clause'])) { $clause = $this->config['clause']; } if (empty($this->config['__path'])) { $this->displayVFSFileList($clause); return; } $this->displayVFSFileSelected($clause); } private function displayVFSFileList($clause) { $this->loadTemplates('displayList'); if (empty($this->table)) { $this->table = WBClass::create('WBDatasource_Table'); } $opt = array(); // specify user account if (!empty($this->config['user'])) { $relation = 'eq'; if ('!' == $this->config['user'][0]) { $relation = 'ne'; $this->config['user'] = substr($this->config['user'], 1); } $clause[] = array( 'field' => $this->table->getIdentifier(WBDatasource::TABLE_USER, true), 'relation' => $relation, 'value' => $this->config['user'] ); } // exclude folders $exId = $this->getExcludedDirId(); if (!empty($exId)) { $clause[] = array( 'field' => $this->table->getIdentifier(WBDatasource::TABLE_VFSDIR, true), 'relation' => 'not_in', 'value' => $exId ); } // list recent files $opt['limit'] = 100; $opt['order'] = array( 'field' => 'created', 'asc' => 0 ); // description is required $clause[] = array( 'field' => 'description', 'relation' => 'not', 'value' => '' ); if (!empty($this->config['mimemajor'])) { $clause[] = array( 'field' => 'mimemajor', 'value' => $this->config['mimemajor'] ); } $list = $this->table->get('vfsfile', null, null, $clause, $opt); $data = array( 'file_count' => count($list) ); $this->tmpl->addGlobalVars($data); if (!$this->tmpl->exists('file_list_entry')) { return; } $this->tmpl->addRows('file_list_entry', $list); } private function getExcludedDirId() { if (empty($this->config['excludedir'])) { return array(); } $exclude = $this->config['excludedir']; if (!is_array($exclude)) { $exclude = array($exclude); } $list = array(); $clause = array(); $clause[] = array( 'field' => 'path', 'relation' => 'begins', 'value' => '' ); $clause[] = array( 'field' => 'uid', 'value' => '' ); if (empty($this->table)) { $this->table = WBClass::create('WBDatasource_Table'); } foreach ($exclude as $ign) { $dir = $this->table->get(WBDatasource::TABLE_VFSDIR, $ign); if (1 != count($dir)) { continue; } $dir = $dir[0]; $clause[0]['value'] = $dir['path']; $clause[1]['value'] = $dir['uid']; $list = array_merge($list, $this->table->getIds(WBDatasource::TABLE_VFSDIR, null, $clause)); } $list = array_values($list); return $list; } private function displayVFSFileSelected($clause) { $this->file = WBClass::create('WBVFS_File'); $this->file = $this->file->loadByObscureId($this->config['__path'][0]); if (!$this->file->isOK()) { $this->loadTemplates('displayInvalid'); return; } // allowed user if (!empty($this->config['user'])) { if ('!' == $this->config['user'][0]) { $relation = 'ne'; $this->config['user'] = substr($this->config['user'], 1); if ($this->file->getUserId() == $this->config['user']) { $this->loadTemplates('displayInvalid'); return; } } else { if ($this->file->getUserId() != $this->config['user']) { $this->loadTemplates('displayInvalid'); return; } } } $exId = $this->getExcludedDirId(); if (!empty($exId) && in_array($this->file->getDirId(), $exId)) { $this->loadTemplates('displayInvalid'); return; } $this->loadTemplates('display'); $data = array( 'id' => $this->file->getId(), 'uid' => $this->file->getUserId(), 'vfsdid' => $this->file->getDirId(), 'mimemajor' => $this->file->getMime(WBVFS_File::MIME_MAJOR), 'mimeminor' => $this->file->getMime(WBVFS_File::MIME_MINOR), 'name' => $this->file->getName(), 'name_uri' => urlencode($this->file->getName()), 'name_slashes' => addslashes($this->file->getName()), 'description' => $this->file->getDescription(), 'obscureid' => $this->file->getObscureId(), 'uri' => $this->file->getUri(), 'url' => $this->file->getUrl(null, true), 'url_download' => $this->file->getUrl('application', true), 'url_image' => $this->file->getUrl('image', true), 'server' => $this->file->getServer(), 'size' => $this->file->getSize(), 'views' => $this->file->getViews(), 'md5' => $this->file->getMd5(), 'reference_count' => 0 ); $this->injectReferences($data); $this->tmpl->addGlobalVars($data); // mangle bubble values if (empty($this->bubbles)) { return; } foreach ($this->bubbles as $name => &$bubble) { $bubble = WBString::populate($bubble, $data); } // deprecated replace using %-syntax $search = array(); $replace = array(); foreach ($data as $key => $value) { $search[] = '%' . $key; $replace[] = $value; } foreach ($this->bubbles as $name => &$bubble) { $bubble = str_replace($search, $replace, $bubble); } } /** * Inject reference count data * * @todo re-implement add gallery support for references * @param array $data of vfsfile */ private function injectReferences(&$data) { $ref = $this->ref->get($data['id']); $data['reference_count'] = count($ref); // add distinct URLs only $urls = array(); $list = array(); foreach ($ref as $r) { if (in_array($r['urlid'], $urls)) { continue; } $urls[] = $r['urlid']; $list[] = $r; } if ($this->tmpl->exists('reference_list_entry')) { $this->tmpl->addRows('reference_list_entry', $list); } } /** * receive output * * fetch output of this content component * * @return string */ public function getString() { return $this->tmpl->getParsedTemplate('snippet'); } /** * mkdir validated * * @param patForms $form * @param array $values * @return bool always false */ public function onMkdirValid($form, $values) { $this->expl->mkDir($this->config['dir'], $values['dirname']); return false; } /** * move folders * * move given folders to new destination * * @param patForms $form * @param array $values * @return bool always false */ public function onMvdirValid($form, $values) { foreach ($values['src'] as $src) { if (0 == $values['des']) { $values['des'] = null; } $this->expl->mvDir($src, $values['des']); } return false; } /** * move files * * move all files to a single folder * * @param patForms $form * @param array $values * @return bool always false */ public function onMvfileValid($form, $values) { $save = array('dir' => $values['des']); foreach ($values['src'] as $src) { $file = $this->file->loadByObscureId($src); $file->save($save); } return false; } /** * start VFS objects * * */ protected function startVFS() { if ($this->file) { return; } if (empty($this->config['user'])) { $this->config['user'] = $this->user->getId(); } // populte user mapping $this->sess->set('vfsfile_user', $this->config['user']); $param = array( 'user' => $this->config['user'] ); $this->file = WBClass::create('WBVFS_File', $param); $this->expl = WBClass::create('WBVFS_Explorer', $param); $this->file->switchTranslation($this->config['translation']); $this->expl->switchTranslation($this->config['translation']); } /** * Create chunk uploader object * * Set user to current user */ private function startChunkUploader() { if ($this->chunk) { return; } $this->chunk = WBClass::create('WBVFS_ChunkUpload'); $this->chunk->setUser($this->user->getId()); } /** * Send response as JSON data * * Pack data in JSON string and send as response. * CAUTION: this method will exit the programme */ private function sendJSONResponse($data) { $json = array( 'data' => $data ); $res = WBClass::create('WBResponse'); /** @var $res WBResponse */ $res->add(json_encode($json)); $res->addHeader('Content-Type', 'text/json'); $res->send($this->req); exit(0); } /** * location of form config * * Return sub directory where form element definitions are located * * @return string folder */ protected function getFormConfigDir() { return 'vfs'; } /** * get element definition for this form * * Use parent's method, but add destination folder for Vfsfile-elements * * @param string $name */ public function getFormElementList($name) { $elements = parent::getFormElementList($name); /* * Simply copy src input values to list of allowed values * The VFSIsDir / VFSIsFile rule does the rest */ if (isset($elements['src'])) { $values = array(); $src = $this->req->get('src', array()); foreach ($src as $s) { $values[] = array( 'value' => $s, 'lable' => $s ); } $elements['src']['attributes']['values'] = $values; } foreach ($elements as &$e) { // add destination folder for new files if ($e['type'] == 'Vfsfile') { $e['attributes']['des'] = $this->config['dir']; $e['attributes']['user'] = $this->config['user']; } } return $elements; } /** * Add form rules to just created form * * For form: "mvdir" and "mvfile" * - verify that destination is a proper folder * - Verify that destination folder is not in source list * - verify that sources are either files or folders * * @param patForms $form object * @param string name of the xml- and template-filename * @param array $elements list of elements current form * @return bool true on success */ protected function insertFormRules(&$form, $name, $elements) { $ruleVfs = null; switch ($name) { case 'mvfile': // rule: VFSIsFile $ruleVfs = patForms::createRule('VFSIsFile'); // fall through case 'mvdir': // rule: VFSIsDir if (!$ruleVfs) { $ruleVfs = patForms::createRule('VFSIsDir'); } $config = array( 'user' => $this->config['user'] ); $ruleVfs->setConfig($config); $src = $form->getElementByName('src'); $src->addRule($ruleVfs); // rule: InOther $ruleInOther = patForms::createRule('InOther'); $config = array( 'in' => 'no', 'other' => $src ); $ruleInOther->setConfig($config); $form->getElementByName('des')->addRule($ruleInOther); break; default: break; } return true; } }