diff --git a/app/Entities/Projects/File.php b/app/Entities/Projects/File.php
new file mode 100644
index 0000000..b18dcee
--- /dev/null
+++ b/app/Entities/Projects/File.php
@@ -0,0 +1,20 @@
+morphTo();
+ }
+
+ public function project()
+ {
+ return $this->morphTo('fileable', Project::class);
+ }
+}
diff --git a/app/Entities/Projects/Project.php b/app/Entities/Projects/Project.php
index 9166e39..c5563c3 100755
--- a/app/Entities/Projects/Project.php
+++ b/app/Entities/Projects/Project.php
@@ -82,4 +82,9 @@ class Project extends Model {
return $overalProgress;
}
+ public function files()
+ {
+ return $this->morphMany(File::class, 'fileable');
+ }
+
}
diff --git a/app/Http/Controllers/Projects/FilesController.php b/app/Http/Controllers/Projects/FilesController.php
new file mode 100644
index 0000000..3960b10
--- /dev/null
+++ b/app/Http/Controllers/Projects/FilesController.php
@@ -0,0 +1,77 @@
+ 'App\Entities\Projects\Project',
+ ];
+
+ public function index(Request $request, $fileableId)
+ {
+ $fileableType = $request->segment(1); // projects, features
+ $modelName = $this->getModelName($fileableType);
+ $modelShortName = $this->getModelShortName($modelName);
+ $model = $modelName::findOrFail($fileableId);
+ $files = $model->files;
+
+ return view($fileableType.'.files', [$modelShortName => $model, 'files' => $files]);
+ }
+
+ public function create(Request $request, $fileableId)
+ {
+ $fileableExist = array_search($request->get('fileable_type'), $this->fileableTypes);
+
+ if ($fileableExist) {
+ $file = $this->proccessPhotoUpload($request->except('_token'), $request->get('fileable_type'), $fileableId);
+
+ if ($file->exists)
+ flash()->success('Upload file berhasil.');
+ else
+ flash()->error('Upload file gagal, coba kembali.');
+ } else
+ flash()->error('Upload file gagal, coba kembali.');
+
+ return back();
+ }
+
+ private function proccessPhotoUpload($data, $fileableType, $fileableId)
+ {
+ // dd(get_class_methods($data['files']));
+ $file = $data['files'];
+ // $fileName = md5(uniqid(rand(), true)).'.'.$file->getClientOriginalExtension();
+ $fileName = $file->hashName();
+ // dd($fileName);
+
+ $fileData['fileable_id'] = $fileableId;
+ $fileData['fileable_type'] = $fileableType;
+ $fileData['filename'] = $fileName;
+ $fileData['title'] = $data['title'];
+ $fileData['description'] = $data['description'];
+ \DB::beginTransaction();
+ // dd(is_dir(storage_path('app/public/files')));
+ $file->storeAs('public/files', $fileName);
+ // $file->move(storage_path('app/public/files'));
+ $file = File::create($fileData);
+ \DB::commit();
+
+ return $file;
+ }
+
+
+ public function getModelName($fileableType)
+ {
+ return isset($this->fileableTypes[$fileableType]) ? $this->fileableTypes[$fileableType] : false;
+ }
+
+ public function getModelShortName($modelName)
+ {
+ return strtolower((new \ReflectionClass($modelName))->getShortName());
+ }
+}
diff --git a/database/migrations/2017_08_03_235706_create_files_table.php b/database/migrations/2017_08_03_235706_create_files_table.php
new file mode 100644
index 0000000..6712f35
--- /dev/null
+++ b/database/migrations/2017_08_03_235706_create_files_table.php
@@ -0,0 +1,37 @@
+increments('id');
+ $table->unsignedInteger('fileable_id');
+ $table->string('fileable_type', 60);
+ $table->tinyInteger('type_id')->unsigned()->nullable();
+ $table->string('filename', 60);
+ $table->string('title', 60);
+ $table->string('description')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('files');
+ }
+}
diff --git a/resources/views/projects/features-export-html.blade.php b/resources/views/projects/features-export-html.blade.php
index ca5a358..fc45327 100755
--- a/resources/views/projects/features-export-html.blade.php
+++ b/resources/views/projects/features-export-html.blade.php
@@ -17,15 +17,15 @@
@foreach($features as $key => $feature)
- {{ $feature->name }}
+ {{ 1 + $key }}. {{ $feature->name }}
- | {{ trans('app.description') }} |
+ | {{ trans('app.description') }} |
| {!! nl2br($feature->description) !!} |
@if ($feature->tasks->count())
| |
-
+
| Sub Fitur |
{{ trans('app.description') }} |
diff --git a/resources/views/projects/files.blade.php b/resources/views/projects/files.blade.php
new file mode 100755
index 0000000..53e5b5c
--- /dev/null
+++ b/resources/views/projects/files.blade.php
@@ -0,0 +1,58 @@
+@extends('layouts.app')
+
+@section('title', trans('project.files') . ' | ' . $project->name)
+
+@section('content')
+@include('projects.partials.breadcrumb',['title' => trans('project.files')])
+
+
+
+@include('projects.partials.nav-tabs')
+
+
+
+
+
{{ trans('project.files') }}
+
+
+
+ | {{ trans('app.table_no') }} |
+ {{ trans('file.title') }} |
+ {{ trans('file.description') }} |
+ {{ trans('app.action') }} |
+
+
+ @forelse($files as $key => $file)
+
+ | {{ 1 + $key }} |
+ {{ $file->title }} |
+ {{ $file->description }} |
+ |
+
+ @empty
+ | {{ trans('file.empty') }} |
+ @endforelse
+
+
+
+
+
+
+
Upload new File
+
+ {!! Form::open(['route' => ['files.upload', $project->id], 'id' => 'upload-files', 'files' => true]) !!}
+ {{ Form::hidden('fileable_type', get_class($project)) }}
+ {!! FormField::file('files', ['label' => 'Upload File', 'placeholder' => 'Pilih File']) !!}
+ {!! FormField::text('title') !!}
+ {!! FormField::textarea('description') !!}
+ {!! Form::submit(trans('app.upload_files'), ['class' => 'btn btn-info']) !!}
+ {!! Form::close() !!}
+
+
+
+
+
+
+@endsection
\ No newline at end of file
diff --git a/resources/views/projects/partials/nav-tabs.blade.php b/resources/views/projects/partials/nav-tabs.blade.php
index 62dd77f..e22a456 100644
--- a/resources/views/projects/partials/nav-tabs.blade.php
+++ b/resources/views/projects/partials/nav-tabs.blade.php
@@ -12,5 +12,8 @@
{!! link_to_route('projects.subscriptions', trans('project.subscriptions'), [$project->id]) !!}
+
+ {!! link_to_route('projects.files', trans('project.files'), [$project->id]) !!}
+
\ No newline at end of file
diff --git a/routes/web/projects.php b/routes/web/projects.php
index 9744c42..00d8213 100644
--- a/routes/web/projects.php
+++ b/routes/web/projects.php
@@ -16,7 +16,6 @@ Route::group(['middleware' => ['web','role:admin'], 'namespace' => 'Projects'],
/**
* Features Routes
*/
-
Route::get('projects/{id}/features/create', ['as'=>'features.create', 'uses'=>'FeaturesController@create']);
Route::get('projects/{id}/features/add-from-other-project', ['as'=>'features.add-from-other-project', 'uses'=>'FeaturesController@addFromOtherProject']);
Route::post('features/{id}/tasks-reorder', ['as'=>'features.tasks-reorder', 'uses'=>'FeaturesController@tasksReorder']);
@@ -32,4 +31,10 @@ Route::group(['middleware' => ['web','role:admin'], 'namespace' => 'Projects'],
Route::post('features/{id}/tasks', ['as'=>'tasks.store', 'uses'=>'TasksController@store']);
Route::patch('task/{id}', ['as'=>'tasks.update', 'uses'=>'TasksController@update']);
Route::delete('task/{id}', ['as'=>'tasks.destroy', 'uses'=>'TasksController@destroy']);
+
+ /**
+ * Files Routes
+ */
+ Route::get('projects/{project}/files', ['as' => 'projects.files', 'uses' => 'FilesController@index']);
+ Route::post('files/{fileable}', ['as' => 'files.upload', 'uses' => 'FilesController@create']);
});
diff --git a/tests/Feature/Projects/UploadFilesTest.php b/tests/Feature/Projects/UploadFilesTest.php
new file mode 100644
index 0000000..652cb8d
--- /dev/null
+++ b/tests/Feature/Projects/UploadFilesTest.php
@@ -0,0 +1,38 @@
+adminUserSigningIn();
+ $project = factory(Project::class)->create(['owner_id' => $user->id]);
+ $this->visit(route('projects.files', $project->id));
+ $this->seeElement('form', ['id' => 'upload-files']);
+ $this->seeElement('input', ['id' => 'files']);
+ $this->seeElement('input', ['type' => 'submit', 'value' => trans('app.upload_files')]);
+
+ $this->attach(storage_path('app/guitar-640.jpg'), 'files');
+ $this->type('Judul file', 'title');
+ $this->type('Deskripsi file yang diuplod.', 'description');
+ $this->press(trans('app.upload_files'));
+
+ $this->assertCount(1, $project->files);
+
+ $this->seeInDatabase('files', [
+ 'fileable_id' => $project->id,
+ 'fileable_type' => 'App\Entities\Projects\Project',
+ 'title' => 'Judul file',
+ 'description' => 'Deskripsi file yang diuplod.',
+ ]);
+
+ $file = $project->files->first();
+ $filePath = storage_path('app/public/files/' . $file->filename);
+ $this->assertFileExistsThenDelete($filePath, 'File doesn\'t exists.');
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
index b7b20ce..4de66f8 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -51,4 +51,12 @@ class TestCase extends \Laravel\BrowserKitTesting\TestCase
return $user;
}
+
+ protected function assertFileExistsThenDelete($filePath)
+ {
+ $this->assertTrue(file_exists($filePath));
+
+ unlink($filePath);
+ $this->assertFalse(file_exists($filePath));
+ }
}
diff --git a/tests/Unit/Models/ProjectTest.php b/tests/Unit/Models/ProjectTest.php
index 11d47e8..5668d4b 100644
--- a/tests/Unit/Models/ProjectTest.php
+++ b/tests/Unit/Models/ProjectTest.php
@@ -4,6 +4,7 @@ namespace Tests\Unit\Models;
use App\Entities\Payments\Payment;
use App\Entities\Projects\Feature;
+use App\Entities\Projects\File;
use App\Entities\Projects\Project;
use App\Entities\Projects\Task;
use App\Entities\Subscriptions\Subscription;
@@ -124,4 +125,11 @@ class ProjectTest extends TestCase
$this->assertEquals(0, $project->getFeatureOveralProgress());
}
+
+ /** @test */
+ public function it_has_many_files()
+ {
+ $project = factory(Project::class)->create();
+ $this->assertTrue($project->files instanceOf Collection);
+ }
}