diff --git a/common/config/bootstrap.php b/common/config/bootstrap.php index d4a7ece..04b959f 100755 --- a/common/config/bootstrap.php +++ b/common/config/bootstrap.php @@ -1,7 +1,8 @@ 'Europe/Kiev', 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', @@ -28,6 +30,9 @@ 'artbox-comment' => [ 'class' => \common\modules\comment\Controller::className(), ], + 'fileloader' => [ + 'class' => FileloaderController::className(), + ], ], 'modules' => [ diff --git a/common/models/Project.php b/common/models/Project.php index 30c7322..651e073 100644 --- a/common/models/Project.php +++ b/common/models/Project.php @@ -3,9 +3,12 @@ namespace common\models; use common\modules\comment\models\CommentProject; + use common\modules\fileloader\behaviors\FileloaderBehavior; + use common\modules\fileloader\models\Fileloader; use Yii; use yii\behaviors\BlameableBehavior; use yii\behaviors\TimestampBehavior; + use yii\db\ActiveQuery; use yii\db\Expression; /** @@ -33,6 +36,8 @@ * @property Currency $budgetCurrency * @property Project $parent * @property int $hidden + * @property int[] $fileloader + * @method File[] getFileloaderFiles() */ class Project extends \yii\db\ActiveRecord { @@ -64,12 +69,15 @@ 'updatedAtAttribute' => false, 'value' => new Expression('NOW()'), ], - 'slug' => [ + 'slug' => [ 'class' => 'common\behaviors\Slug', 'in_attribute' => 'name', 'out_attribute' => 'link', 'translit' => true, ], + 'fileloader' => [ + 'class' => FileloaderBehavior::className(), + ], ]; } @@ -154,21 +162,21 @@ 'boolean', ], [ - ['hidden'], + [ 'hidden' ], 'default', 'value' => 0, ], [ - ['date_end'], + [ 'date_end' ], 'filter', 'filter' => function($value) { $unix = strtotime($value); if($unix <= time()) { - $unix = time() + (3600 * 24 * 7); + $unix = time() + ( 3600 * 24 * 7 ); } return date('Y-m-d', $unix); - } - ] + }, + ], ]; } @@ -332,4 +340,5 @@ return $this->hasMany(CommentProject::className(), [ 'model_id' => 'project_id' ]) ->andWhere([ 'model' => $this->className() ]); } + } diff --git a/common/modules/fileloader/Module.php b/common/modules/fileloader/Module.php new file mode 100644 index 0000000..a93f91d --- /dev/null +++ b/common/modules/fileloader/Module.php @@ -0,0 +1,23 @@ + View::POS_HEAD, + ]; + + } \ No newline at end of file diff --git a/common/modules/fileloader/behaviors/FileloaderBehavior.php b/common/modules/fileloader/behaviors/FileloaderBehavior.php new file mode 100644 index 0000000..8466e9b --- /dev/null +++ b/common/modules/fileloader/behaviors/FileloaderBehavior.php @@ -0,0 +1,116 @@ + 'afterSave', + ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave', + ActiveRecord::EVENT_INIT => 'attachValidator', + ]; + } + + public function attachValidator($event) + { + $validator = Validator::createValidator('safe', $this->owner, 'fileloader'); + $this->owner->validators->append($validator); + } + + /** + * After saving model delete all relative files and insert new file connections from + * fileloader variable + * + * @param Event $event + */ + public function afterSave($event) + { + /** + * @var ActiveRecord $owner + * @var string $relation + */ + $owner = $this->owner; + $relation = $this->relationclass; + call_user_func([ + $relation, + 'deleteAll', + ], [ + 'model' => $owner->className(), + 'model_id' => $owner->primaryKey, + ]); + if(!empty( $owner->fileloader )) { + foreach($owner->fileloader as $file) { + /** + * @var ActiveRecord $model + */ + $model = new $relation([ + 'file_id' => $file, + 'model' => $owner->className(), + 'model_id' => $owner->primaryKey, + 'user_id' => \Yii::$app->user->getId(), + 'status' => 1, + ]); + if($model->validate()) { + $model->save(false); + } + unset( $model ); + } + } + } + + /** + * Bind owner class tp specified $fileclass via $relationclass table. + * @return ActiveQuery + */ + public function getFileloaderFiles() + { + /** + * @var ActiveRecord $owner + */ + $owner = $this->owner; + $relationtable = call_user_func([ + $this->relationclass, + 'tableName', + ]); + return $owner->hasMany($this->fileclass, [ 'file_id' => 'file_id' ]) + ->viaTable($relationtable, [ 'model_id' => $owner->primaryKey()[ 0 ] ], function($query) use ($owner) { + /** + * @var ActiveQuery $query + */ + $query->andWhere([ + 'model' => $owner->className(), + 'status' => 1, + ]); + }); + } + } \ No newline at end of file diff --git a/common/modules/fileloader/controllers/FileloaderController.php b/common/modules/fileloader/controllers/FileloaderController.php new file mode 100644 index 0000000..ff7875d --- /dev/null +++ b/common/modules/fileloader/controllers/FileloaderController.php @@ -0,0 +1,105 @@ + [ + 'class' => AccessControl::className(), + 'rules' => [ + [ + 'allow' => true, + 'roles' => [ '@' ], + ], + ], + ], + 'verbs' => [ + 'class' => \yii\filters\VerbFilter::className(), + 'actions' => [ + '*' => [ 'post' ], + ], + ], + ]; + } + + /** + * Handle ajax file uploading + * + * @return array + */ + public function actionUpload() + { + $request = \Yii::$app->request; + $response = \Yii::$app->response; + $response->format = $response::FORMAT_JSON; + $model = new Fileloader(); + $model->files = UploadedFile::getInstance($model, 'files'); + if(!empty( $model->files )) { + $file_id = $model->saveFile($model->files); + if(!empty( $file_id )) { + $child_model = $request->post('model', $model->className()); + $child_model = new $child_model([ 'file' => $file_id ]); + $input = Html::activeHiddenInput($child_model, 'fileloader[]', [ + 'value' => $file_id, + 'class' => 'fileloader-item-input', + ]); + return [ + 'result' => [ + 'file_id' => $file_id, + 'file_name' => $model->name, + 'file_href' => $model->dir, + 'input' => $input, + 'id' => $request->post('id'), + ], + ]; + } else { + return [ 'error' => 'Ошибка сохранения файла' ]; + } + } + return [ 'error' => 'Ошибка загрузки' ]; + } + + /** + * Handle ajax file deleting + * + * @return array + */ + public function actionDelete() + { + /** + * @var Fileloader $model + */ + $request = \Yii::$app->request; + $response = \Yii::$app->response; + $response->format = $response::FORMAT_JSON; + if(empty( $request->post('id') )) { + return [ 'error' => 'Не указан id файла' ]; + } + $model = Fileloader::find() + ->where([ + 'file_id' => $request->post('id'), + 'user_id' => \Yii::$app->user->getId(), + ]) + ->one(); + if(empty( $model )) { + return [ 'error' => 'Файл не найден' ]; + } + if($model->delete()) { + return [ 'result' => [ 'message' => 'Файл успешно удален' ] ]; + } else { + return [ 'error' => 'Ошибка удаления файла' ]; + } + } + + } \ No newline at end of file diff --git a/common/modules/fileloader/models/FileRelation.php b/common/modules/fileloader/models/FileRelation.php new file mode 100644 index 0000000..15db765 --- /dev/null +++ b/common/modules/fileloader/models/FileRelation.php @@ -0,0 +1,79 @@ + 255], + [['file_id'], 'exist', 'skipOnError' => true, 'targetClass' => File::className(), 'targetAttribute' => ['file_id' => 'file_id']], + [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'file_relation_id' => Yii::t('app', 'File Relation ID'), + 'file_id' => Yii::t('app', 'File ID'), + 'model' => Yii::t('app', 'Model'), + 'model_id' => Yii::t('app', 'Model ID'), + 'user_id' => Yii::t('app', 'User ID'), + 'date_add' => Yii::t('app', 'Date Add'), + 'status' => Yii::t('app', 'Status'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getFile() + { + return $this->hasOne(File::className(), ['file_id' => 'file_id']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getUser() + { + return $this->hasOne(User::className(), ['id' => 'user_id']); + } +} diff --git a/common/modules/fileloader/models/Fileloader.php b/common/modules/fileloader/models/Fileloader.php new file mode 100644 index 0000000..03eba2a --- /dev/null +++ b/common/modules/fileloader/models/Fileloader.php @@ -0,0 +1,103 @@ + BlameableBehavior::className(), + 'createdByAttribute' => 'user_id', + 'updatedByAttribute' => false, + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [['status'], 'integer'], + [['name'], 'string', 'max' => 50], + [['dir'], 'string', 'max' => 255], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'file_id' => 'File ID', + 'status' => 'Status', + 'name' => 'Name', + 'dir' => 'Dir', + ]; + } + + /** + * @param UploadedFile $file + * @return int file id in model File + */ + public function saveFile(UploadedFile $file){ + $imgDir = Yii::getAlias('@storage/'.'user_'.\Yii::$app->user->id.'/files/'); + $uploadName = preg_replace('/\s/', '_', $file->baseName).'_'. time().'.'.$file->extension; + $uploadName = Tools::translit($uploadName, 'letter'); + if(!is_dir($imgDir)) { + mkdir($imgDir, 0755, true); + } + if($file->saveAs($imgDir.$uploadName)){ + $this->dir = '/storage/user_'.\Yii::$app->user->id.'/files/'.$uploadName; + $this->name = preg_replace('/\s/', '_', $file->baseName).'.'.$file->extension; + $this->save(); + return $this->file_id; + } + } + + /** + * Extends ActiveRecord::delete() method, also deletes file from file system + * + * @see ActiveRecord::delete() + * @return false|int + * @throws \Exception + */ + public function delete() + { + if(!empty($this->dir)) { + if(file_exists(Yii::getAlias('@documentRoot').$this->dir)) { + unlink(Yii::getAlias('@documentRoot').$this->dir); + } + } + return parent::delete(); + } +} diff --git a/common/modules/fileloader/resources/fileloader.css b/common/modules/fileloader/resources/fileloader.css new file mode 100644 index 0000000..effe6f0 --- /dev/null +++ b/common/modules/fileloader/resources/fileloader.css @@ -0,0 +1,17 @@ +.fileloader-item-name { + display: inline-block; +} +.fileloader-item-name a { + text-decoration: none; + border-bottom: dotted 1px; +} +.fileloader-item-name a:hover, .fileloader-item-name a:focus { + text-decoration: none; +} +.fileloader-item-remove { + padding-left: 10px; + cursor: pointer; +} +.fileloader-file { + position: relative; +} \ No newline at end of file diff --git a/common/modules/fileloader/resources/handler.js b/common/modules/fileloader/resources/handler.js new file mode 100644 index 0000000..d63bb5d --- /dev/null +++ b/common/modules/fileloader/resources/handler.js @@ -0,0 +1,45 @@ +$(function() { + if(fileloader !== undefined) { + $.each(fileloader, function(index, value) { + var id = value.id; + var model = value.model; + var formData = {}; + if(typeof model == 'string' && model !== '') { + formData.model = model; + } + if(typeof id == 'string' && id !== '') { + formData.id = id; + $('#'+id).fileupload({ + dataType: 'json', + url: '/fileloader/upload', + formData: formData, + done: function(e, data) { + if(!data.result.error) { + var id = data.result.result.id; + var input = $('#'+id); + var wrapper = $(input).parents('.fileloader-wrapper').first(); + var html = '
Лучшие компании и исполнители готовы помочь вам
-Лучшие компании и исполнители готовы помочь вам
- Частные проекты и субподряды от лучших компаний и заказчиков для вас -
-- = Html::a('Ищу работу',['landing/landing-work']) ?> - Вакансии компаний и заказчиков на полную занятость и резюме свободных проектантов -
-+ Частные проекты и субподряды от лучших компаний и заказчиков для вас +
++ = Html::a('Ищу работу', [ 'landing/landing-work' ]) ?> + Вакансии компаний и заказчиков на полную занятость и резюме свободных проектантов +
Презентуйте лучшую команду профессиональных проектантов и получите актуальные тендеры на проектирование.
-Презентуйте лучшую команду профессиональных проектантов и получите актуальные тендеры на проектирование.