%PDF- %PDF-
| Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/cfiles/models/ |
| Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/modules/cfiles/models/Folder.php |
<?php
namespace humhub\modules\cfiles\models;
use humhub\modules\content\components\ContentContainerActiveRecord;
use humhub\modules\content\models\Content;
use humhub\modules\file\libs\ImageHelper;
use humhub\modules\file\models\FileContent;
use humhub\modules\file\libs\FileHelper;
use humhub\modules\user\models\User;
use humhub\modules\search\events\SearchAddEvent;
use humhub\modules\space\models\Space;
use Yii;
use yii\db\ActiveQuery;
use yii\imagine\Image;
use yii\web\UploadedFile;
/**
* This is the model class for table "cfiles_folder".
*
* @property integer $id
* @property integer $parent_folder_id
* @property string $title
* @property string $description
* @property string $type
*
* @property Folder parentFolder
* @property Folder[] subFolders
* @property Folder[] specialFolders
* @property File[] subFiles
*
*/
class Folder extends FileSystemItem
{
const TYPE_FOLDER_ROOT = 'root';
const TYPE_FOLDER_POSTED = 'posted';
const ROOT_TITLE = 'Root';
const ROOT_DESCRIPTION = 'The root folder is the entry point that contains all available files.';
const ALL_POSTED_FILES_TITLE = 'Files from the stream';
const ALL_POSTED_FILES_DESCRIPTION = 'You can find all files that have been posted to this stream here.';
/**
* @inheritdoc
*/
public $wallEntryClass = "humhub\modules\cfiles\widgets\WallEntryFolder";
/**
* @inheritdoc
*/
public $streamChannel = null;
/**
* @inheritdoc
*/
public static function tableName()
{
return 'cfiles_folder';
}
/**
* @inheritdoc
*/
public function getContentName()
{
return Yii::t('CfilesModule.base', "Folder");
}
/**
* @inheritdoc
*/
public function getIcon()
{
return'fa-folder';
}
/**
* @inheritdoc
*/
public function rules()
{
$result = array_merge(parent::rules(), [
['parent_folder_id', 'integer'],
['parent_folder_id', 'validateParentFolderId'],
['title', 'required'],
['title', 'trim'],
['title', 'string', 'min' => 1, 'max' => 255],
['title', 'noSpaces'],
['description', 'string', 'max' => 255],
['title', 'uniqueTitle']
]);
if (!$this->isRoot()) {
$result[] = ['parent_folder_id', 'required'];
}
return $result;
}
/**
* Makes sure that after an title change there is no equal title for the given container in the given parent folder.
*
* @param string $attribute
* @param array $params
* @param string $validator
*/
public function uniqueTitle($attribute, $params, $validator)
{
if ($this->isRoot() || !$this->hasTitleChanged()) {
return;
}
if ($this->parentFolder->folderExists($this->title)) {
$this->addError('title', \Yii::t('CfilesModule.base', 'A folder with this name already exists.'));
}
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), [
'id' => 'ID',
'parent_folder_id' => Yii::t('CfilesModule.models_Folder', 'Parent Folder ID'),
'title' => Yii::t('CfilesModule.models_Folder', 'Title'),
'description' => Yii::t('CfilesModule.models_Folder', 'Description')
]);
}
/**
* @inheritdoc
*/
public function attributeHints()
{
if (!$this->isNewRecord) {
return ['visibility' => Yii::t('CfilesModule.models_FileSystemItem', 'Note: Changes of the folders visibility, will be inherited by all contained files and folders.')];
}
return parent::attributeHints();
}
/**
* @inheritdoc
*/
public function getSearchAttributes()
{
if ($this->isAllPostedFiles() || $this->isRoot()) {
$attributes = [];
} else {
$attributes = [
'name' => $this->title,
'description' => $this->description
];
if($this->getCreator()) {
$attributes['creator'] = $this->getCreator()->getDisplayName();
}
if($this->getEditor()) {
$attributes['editor'] = $this->getEditor()->getDisplayName();
}
}
$this->trigger(self::EVENT_SEARCH_ADD, new SearchAddEvent($attributes));
return $attributes;
}
/**
* @inheritdoc
*/
public function beforeSave($insert)
{
if ($insert && $this->visibility !== null) {
$this->content->visibility = $this->visibility;
} else if ($this->visibility !== null && $this->visibility != $this->content->visibility) {
$this->updateVisibility($this->visibility);
}
return parent::beforeSave($insert);
}
/**
* @inheritdoc
*/
public function afterMove(ContentContainerActiveRecord $container = null)
{
parent::afterMove($container);
// Move all sub folders and files into the same Container where this Folder has been moved to
$this->moveSubFoldersToContainer($container);
$this->moveSubFilesToContainer($container);
}
public function moveSubFoldersToContainer(ContentContainerActiveRecord $container = null)
{
if ($container === null) {
$container = $this->content->getContainer();
}
$folders = Folder::find()
->andWhere(['parent_folder_id' => $this->id])
->all();
foreach ($folders as $folder) {
/* @var Folder $folder */
$folder->move($container);
}
}
public function moveSubFilesToContainer(ContentContainerActiveRecord $container = null)
{
if ($container === null) {
$container = $this->content->getContainer();
}
$files = File::find()
->joinWith('baseFile')
->andWhere(['cfiles_file.parent_folder_id' => $this->id])
->all();
foreach ($files as $file) {
/* @var File $file */
$file->move($container);
}
}
/**
* @param $visibility
*/
public function updateVisibility($visibility)
{
if ($visibility === null) {
return;
}
$this->content->visibility = $visibility;
foreach ($this->getSubFiles() as $file) {
$file->content->visibility = $visibility;
$file->content->save();
}
foreach ($this->getSubFolders() as $folder) {
$folder->updateVisibility($visibility);
$folder->content->save();
}
}
/**
* @inheritdoc
*/
public function beforeDelete()
{
foreach ($this->folders as $folder) {
$folder->delete();
}
foreach ($this->files as $file) {
$file->delete();
}
return parent::beforeDelete();
}
public function getVisibilityTitle()
{
if (Yii::$app->getModule('friendship')->getIsEnabled() && $this->content->container instanceof User) {
if ($this->content->container->isCurrentuser()) {
$privateText = Yii::t('CfilesModule.base', 'This folder is only visible for you and your friends.');
} else {
$privateText = Yii::t('CfilesModule.base', 'This folder is protected.');
}
return $this->content->isPublic() ? Yii::t('CfilesModule.base', 'This folder is public.') : $privateText;
}
return $this->content->isPublic() ? Yii::t('CfilesModule.base', 'This folder is public.') : Yii::t('CfilesModule.base', 'This folder is private.');
}
/**
* In older versions there was no actual root folder, all root files and folders had parent_folder_id 0 or null.
* This function can be executed for newly created root folders and will move all files/folders to the new root.
*/
public function migrateFromOldStructure()
{
if (!$this->isRoot()) {
return;
}
$filesQuery = File::find()->joinWith('baseFile')->contentContainer($this->content->container)
->andWhere(['OR', ['IS', 'cfiles_file.parent_folder_id', new \yii\db\Expression('NULL')], ['cfiles_file.parent_folder_id' => 0]]);
$foldersQuery = Folder::find()->contentContainer($this->content->container)
->andWhere(['OR', ['IS', 'cfiles_folder.parent_folder_id', new \yii\db\Expression('NULL')], ['cfiles_folder.parent_folder_id' => 0]])
->andWhere(['IS', 'cfiles_folder.type', new \yii\db\Expression('NULL')]);
$rootsubfiles = $filesQuery->all();
$rootsubfolders = $foldersQuery->all();
foreach ($rootsubfiles as $file) {
$file->parent_folder_id = $this->id;
$file->save();
}
foreach ($rootsubfolders as $folder) {
$folder->parent_folder_id = $this->id;
$folder->save();
}
}
/**
* Initializes a root folder for the given $contentContainer
* @param ContentContainerActiveRecord $contentContainer
* @return Folder|boolean
*/
public static function initRoot(ContentContainerActiveRecord $contentContainer)
{
if (!empty(self::getRoot($contentContainer))) {
return false;
}
$root = new self($contentContainer, Content::VISIBILITY_PUBLIC, [
'type' => self::TYPE_FOLDER_ROOT,
'title' => self::ROOT_TITLE,
'description' => self::ROOT_DESCRIPTION
]);
$root->content->created_by = self::getContainerOwnerId($contentContainer);
$root->silentContentCreation = true;
if ($root->save()) {
return $root;
}
return false;
}
/**
* Initializes the posted files folder for the given $contentContainer
* @param ContentContainerActiveRecord $contentContainer
* @return bool|Folder
*/
public static function initPostedFilesFolder(ContentContainerActiveRecord $contentContainer)
{
$root = self::getRoot($contentContainer);
if (!$root || !empty(self::getPostedFilesFolder($contentContainer))) {
return false;
}
$postedFilesFolder = new self($contentContainer, Content::VISIBILITY_PUBLIC, [
'type' => self::TYPE_FOLDER_POSTED,
'title' => self::ALL_POSTED_FILES_TITLE,
'description' => self::ALL_POSTED_FILES_DESCRIPTION,
'parent_folder_id' => $root->id,
]);
$postedFilesFolder->content->created_by = self::getContainerOwnerId($contentContainer);
$postedFilesFolder->silentContentCreation = true;
if ($postedFilesFolder->save()) {
return $postedFilesFolder;
}
return false;
}
/**
* Generate the maximum depth directory structure originating from a given folder id.
*
* @param Folder $parentId
* @return array [['folder' => --current folder--, 'subfolders' => [['folder' => --current folder--, 'subfolders' => []], ...], ['folder' => --current folder--, 'subfolders' => [['folder' => --current folder--, 'subfolders' => []], ...], ...]
*/
public static function getFolderList($parent, $orderBy = ['title' => SORT_ASC])
{
$parentId = ($parent instanceof Folder) ? $parent->id : $parent;
$dirStruc = [];
foreach (self::getSubFoldersByParent($parent, $orderBy)->all() as $folder) {
$dirStruc[] = ['folder' => $folder, 'subfolders' => self::getFolderlist($folder, $orderBy)];
}
return $dirStruc;
}
/**
* Returns all readable subfolders of the given parent folder.
*
* @param $contentContainer
* @param Folder $parent
* @param array $orderBy
* @return ActiveQuery
*/
public static function getSubFoldersByParent($parent, $orderBy = ['title' => SORT_ASC])
{
$query = Folder::find()->contentContainer($parent->content->container)->readable();
$query->andWhere(['cfiles_folder.parent_folder_id' => $parent->id]);
// do not return any subfolders here that are root or allpostedfiles
$query->andWhere([
'or',
['cfiles_folder.type' => null],
['and',
['<>', 'cfiles_folder.type', Folder::TYPE_FOLDER_POSTED],
['<>', 'cfiles_folder.type', Folder::TYPE_FOLDER_ROOT]
]
]);
return $query->orderBy($orderBy);
}
/**
* @param ContentContainerActiveRecord $contentContainer
* @return int
*/
private static function getContainerOwnerId(ContentContainerActiveRecord $contentContainer)
{
if ($contentContainer instanceof User) {
return $contentContainer->id;
} else if ($contentContainer instanceof Space) {
return $contentContainer->created_by;
}
return null;
}
/**
* @param ContentContainerActiveRecord $contentContainer
* @return Folder the root folder of the given ContentContainerActiveRecord
*/
public static function getOrInitRoot(ContentContainerActiveRecord $contentContainer)
{
if ($root = Folder::getRoot($contentContainer)) {
return $root;
}
return Folder::initRoot($contentContainer);
}
/**
* @param ContentContainerActiveRecord $contentContainer
* @return Folder the root folder of the given ContentContainerActiveRecord
*/
public static function getRoot(ContentContainerActiveRecord $contentContainer)
{
return self::find()->contentContainer($contentContainer)->andWhere(['type' => self::TYPE_FOLDER_ROOT])->one();
}
/**
* @param ContentContainerActiveRecord $contentContainer
* @return Folder the root folder of the given ContentContainerActiveRecord
*/
public static function getPostedFilesFolder(ContentContainerActiveRecord $contentContainer)
{
return self::find()->contentContainer($contentContainer)->andWhere(['type' => self::TYPE_FOLDER_POSTED])->one();
}
/**
* @return ActiveQuery of all direct child files
*/
public function getFiles()
{
return $this->hasMany(File::className(), ['parent_folder_id' => 'id'])
->joinWith('baseFile')
->orderBy(['title' => SORT_ASC]);
}
/**
* @return ActiveQuery of all direct child folders
*/
public function getFolders()
{
return $this->hasMany(Folder::className(), ['parent_folder_id' => 'id'])->orderBy(['title' => SORT_ASC]);
}
/**
* @return boolean
*/
public function hasTitleChanged()
{
return $this->isNewRecord || $this->getOldAttribute('title') != $this->title;
}
/**
* @return string
*/
public function getItemId()
{
return $this->getItemType() . '_' . $this->id;
}
/**
* @inheritdoc
*/
public function getContentId()
{
return $this->content->id;
}
/**
* @return string
*/
public function getItemType()
{
return 'folder' . ($this->type !== null ? '-' . $this->type : '');
}
public function getTitle()
{
if ($this->isRoot()) {
return Yii::t('CfilesModule.base', 'Root');
} else if ($this->isAllPostedFiles()) {
return Yii::t('CfilesModule.base', 'Files from the stream');
}
return $this->title;
}
public function getDescription()
{
if ($this->isRoot()) {
return Yii::t('CfilesModule.base', 'The root folder is the entry point that contains all available files.');
} else if ($this->isAllPostedFiles()) {
return Yii::t('CfilesModule.base', 'You can find all files that have been posted to this stream here.');
}
return $this->description;
}
/**
* @return string
*/
public function getDownloadCount()
{
return '';
}
public function getSize()
{
return 0;
}
public function createUrl($route = null, $params = [], $scheme = false)
{
$params = (is_array($params)) ? $params : [];
$params['fid'] = $this->id;
return $this->content->container->createUrl($route, $params, $scheme);
}
public function getUrl()
{
if (empty($this->content->container)) {
return "";
}
return $this->content->container->createUrl('/cfiles/browse/index', ['fid' => $this->id]);
}
public function getFullUrl()
{
if (empty($this->content->container)) {
return "";
}
return $this->content->container->createUrl('/cfiles/browse/index', ['fid' => $this->id], true);
}
public function getEditUrl()
{
return $this->content->container->createUrl('/cfiles/edit/folder', ['id' => $this->getItemId()]);
}
public function noSpaces($attribute, $params)
{
if (trim($this->$attribute) !== $this->$attribute) {
$this->addError($attribute, Yii::t('CfilesModule.base', 'Folder should not start or end with blank space.'));
}
}
public function getFullPath($separator = '/')
{
return $this->getPathFromId($this->id, false, $separator);
}
public static function getPathFromId($id, $parentFolderPath = false, $separator = '/', $withRoot = false)
{
if ($id == 0) {
return $separator;
}
$item = Folder::findOne([
'id' => $id
]);
if (empty($item)) {
return null;
}
$tempFolder = $item->parentFolder;
$path = '';
if (!$parentFolderPath) {
if ($item->isRoot()) {
if ($withRoot) {
$path .= $item->title;
}
} else {
$path .= $separator . $item->title;
}
}
$counter = 0;
// break at maxdepth to avoid hangs
while (!empty($tempFolder)) {
if ($tempFolder->isRoot()) {
if ($withRoot) {
$path = $tempFolder->title . $path;
}
break;
} else {
if (++$counter > 10) {
$path = '...' . $path;
break;
}
$path = $separator . $tempFolder->title . $path;
}
$tempFolder = $tempFolder->parentFolder;
}
return $path;
}
/**
* Returns the folder path as ordered array.
* @return Folder[]
*/
public function getCrumb()
{
$parent = $this;
do {
$crumb[] = $parent;
$parent = $parent->parentFolder;
} while ($parent != null);
return array_reverse($crumb);
}
/**
* @inheritdoc
*/
public function getContentDescription()
{
return $this->title;
}
public function isRoot()
{
return $this->type === self::TYPE_FOLDER_ROOT;
}
public function isAllPostedFiles()
{
return $this->type === self::TYPE_FOLDER_POSTED;
}
/**
* Validate parent folder id
*
* @param string $attribute the attribute name
*/
public function validateParentFolderId($attribute = 'parent_folder_id')
{
$parent = $this->parentFolder;
// check if one of the parents is oneself to avoid circles
while (!empty($parent)) {
if ($this->id == $parent->id) {
$this->addError($attribute, Yii::t('CfilesModule.base', 'Please select a valid destination folder for %title%.', ['%title%' => $this->title]));
break;
}
$parent = static::findOne(['id' => $parent->parent_folder_id]);
}
parent::validateParentFolderId($attribute);
}
/**
* @return FileSystemItem[] return all child folders and child files excluding special folders
*/
public function getChildren()
{
return array_merge($this->getSubFolders(), $this->getSubFiles());
}
/**
* @param array $order
* @return Folder[]
*/
public function getSpecialFolders()
{
$specialFoldersQuery = Folder::find()->contentContainer($this->content->container)->readable();
$specialFoldersQuery->andWhere(['cfiles_folder.parent_folder_id' => $this->id]);
$specialFoldersQuery->andWhere(['is not', 'cfiles_folder.type', null]);
return $specialFoldersQuery->all();
}
/**
* @return Folder[]
*/
public function getSubFolders($order = 'title ASC')
{
return self::getSubFoldersByParent($this, $order)->all();
}
/**
* @param null $order
* @return File[]
*/
public function getSubFiles($order = 'file.file_name ASC')
{
$filesQuery = File::find()->joinWith('baseFile')->contentContainer($this->content->container)->readable();
$filesQuery->andWhere(['cfiles_file.parent_folder_id' => $this->id]);
$filesQuery->orderBy($order);
return $filesQuery->all();
}
/**
* Creates and adds the given UploadedFile to this directory.
*
* Returns the newly created cfiles file.
* The calling function has to make sure there are no errors by checking_
*
* ```php
* $file->hasErrors()
* ```
* and
*
* ```php
* $file->baseFile->hasErrors();
* ```
* @param UploadedFile $uploadedFile
* @return File
*/
public function addUploadedFile(UploadedFile $uploadedFile): File
{
// Get file instance either an existing one or a new one
$file = $this->getFileInstance($uploadedFile);
if ($file->setUploadedFile($uploadedFile)) {
$file->save();
}
return $file;
}
/**
* @param UploadedFile $uploadedFile
* @return File
*/
private function getFileInstance(UploadedFile $uploadedFile): File
{
if ($file = $this->findFileByName($uploadedFile->name)) {
return $file;
}
return new File($this->content->container, $this->getNewItemVisibility(), [
'parent_folder_id' => $this->id
]);
}
private function getNewItemVisibility()
{
if ($this->isRoot()) {
return $this->content->container->getDefaultContentVisibility();
}
return $this->content->visibility;
}
public function addFileFromPath($filename, $filePath)
{
$file = new File($this->content->container, $this->getNewItemVisibility(), [
'parent_folder_id' => $this->id
]);
$fileContent = new FileContent([
'mime_type' => FileHelper::getMimeType($filePath),
'size' => filesize($filePath),
'show_in_stream' => 0,
'file_name' => $this->getAddedFileName($filename)
]);
if ($fileContent->mime_type == 'image/jpeg') {
$image = Image::getImagine()->open($filePath);
ImageHelper::fixJpegOrientation($image, $filePath);
}
$fileContent->newFileContent = stream_get_contents(fopen($filePath, 'r'));
$file->setFileContent($fileContent);
$file->save();
return $file;
}
/**
* Creates a new non persisted folder within this folder.
*
* @param string|null $title
* @param string|null $description
* @return Folder
*/
public function newFolder($title = null, $description = null)
{
return new self($this->content->container, $this->getNewItemVisibility(), [
'parent_folder_id' => $this->id,
'title' => $title,
'description' => $description]);
}
/**
* Moves the given item into this folder.
*
* This method checks for duplicate file/folder names.
*
* If a file with the same title already exists we use a file name index e.g. file(1).txt
*
* If a folder already exists with the same title we merge all sub items into the existing folder
*
*
* @param FileSystemItem $item
* @return bool
*/
public function moveItem(FileSystemItem $item)
{
if (!$item) {
return false;
}
if (!$item->canEdit()) {
if ($item instanceof File) {
$item->addError($item->getTitle(), Yii::t('CfilesModule.base', 'You cannot move the file "{name}"!', ['name' => $item->getTitle()]));
} else {
$item->addError($item->getTitle(), Yii::t('CfilesModule.base', 'You cannot move the folder "{name}"!', ['name' => $item->getTitle()]));
}
return false;
}
if ($item instanceof Folder && !$item->isEditableFolder()) {
$item->addError($item->getTitle(), Yii::t('CfilesModule.base', 'Folder {name} given folder is not editable!', ['name' => $item->getTitle()]));
return false;
}
if ($item->getItemId() === $this->getItemId()) {
$item->addError($item->getTitle(), Yii::t('CfilesModule.base', 'Folder {name} can\'t be moved to itself!', ['name' => $item->getTitle()]));
return false;
}
// We ignore invalid items and items already residing in the given destination
if ($item->hasParent($this) || $item->is($this)) {
return true;
}
// Note we don't set the content visibility directly to run recursive visibility change in folders
$item->visibility = $this->content->visibility;
$item->parent_folder_id = $this->id;
$moveResult = $this->checkForDuplicate($item);
if (!$moveResult) {
// Probably an error when moving subfiles to an existing folder
return false;
}
if ($item->is($moveResult)) {
// Either no duplicate or just simple file rename
return $moveResult->save();
}
// Successfully moved subfiles to existing folder with same title
return true;
}
/**
* Checks if the given $item title already exists in this folder and renames already existing files or
* merges already existing folders.
*
* This method returns either the item itself in case there was no duplicate or a file duplicate (which was renamed)
* or the already existing folder in case the item is a folder with the same title as an existing sub folder,
* or null in case there was an error when moving files to an existing subfolder.
*
* @param FileSystemItem $item
* @return FileSystemItem|null
*/
private function checkForDuplicate(FileSystemItem $item)
{
$result = null;
if ($item instanceof File) {
$item->setTitle($this->getAddedFileName($item->getTitle()));
$result = $item;
} else if ($item instanceof Folder) {
$result = $item;
$existingFolderWithTitle = $this->findFolderByName($item->title);
// Check if the folder exists if not, move children to existing subfolder, if there is an error we set §result to null
if ($existingFolderWithTitle && !$existingFolderWithTitle->is($item)) {
$result = $existingFolderWithTitle;
foreach ($item->getChildren() as $child) {
// if moving the given item fails we set result to null and add an item error
if (!$existingFolderWithTitle->moveItem($child)) {
$result = null;
foreach ($child->getErrors() as $attribute => $errors) {
$item->addErrors([$child->getTitle() => $errors]);
}
};
}
if ($result) {
$item->delete();
}
}
}
return $result;
}
/**
* Searches for direct sub files with the given file name and returns an indexed file name in form of
* myFile(<index>).txt in case an existing file was found, otherwise the original fileName is returned.
*
* @param $fileName
* @return string either an indexed file name or original filename if no duplicate title was found.
*/
protected function getAddedFileName($fileName)
{
$counter = 0;
$parts = preg_split('~\.(?=[^\.]*$)~', $fileName);
$origName = $parts[0];
$ext = sizeof($parts) == 2 ? '.' . $parts[1] : '';
while ($this->fileExists($fileName)) {
$fileName = $origName . '(' . ++$counter . ')' . $ext;
}
return $fileName;
}
public function fileExists($name)
{
return File::find()->joinWith('baseFile')->where(['file_name' => $name, 'cfiles_file.parent_folder_id' => $this->id])->count();
}
public function folderExists($name)
{
return Folder::find()->where(['title' => $name, 'parent_folder_id' => $this->id])->count();
}
public function findFileByName($name): ?File
{
return File::find()->contentContainer($this->content->container)
->joinWith('baseFile')
->andWhere(['file_name' => $name])
->andWhere(['cfiles_file.parent_folder_id' => $this->id])
->one();
}
public function findFolderByName($name)
{
return Folder::find()->contentContainer($this->content->container)
->andWhere(['title' => $name, 'parent_folder_id' => $this->id])->one();
}
/**
* @inheritdoc
*/
public function getVersionsUrl(int $versionId = 0): ?string
{
return null;
}
/**
* @inheritdoc
*/
public function getDeleteVersionUrl(int $versionId): ?string
{
return null;
}
}