diff --git a/config/media-library.php b/config/media-library.php index ae7004f83..c741505fe 100644 --- a/config/media-library.php +++ b/config/media-library.php @@ -31,4 +31,5 @@ 'prefix_uuid_with_local_path' => config('twill.file_library.prefix_uuid_with_local_path', false), 'translated_form_fields' => false, 'show_file_name' => false, + 'show_media_references' => false, ]; diff --git a/frontend/js/components/MediaField.vue b/frontend/js/components/MediaField.vue index fb128e0d6..b748e0e8e 100755 --- a/frontend/js/components/MediaField.vue +++ b/frontend/js/components/MediaField.vue @@ -30,6 +30,12 @@
  • {{ metadatas.text }}
  • +
  • + +
  • @@ -256,7 +262,8 @@ }, ...mapState({ selectedMedias: state => state.mediaLibrary.selected, - allCrops: state => state.mediaLibrary.crops + allCrops: state => state.mediaLibrary.crops, + showMediaReferences: state => state.mediaLibrary.showMediaReferences }) }, watch: { @@ -804,6 +811,10 @@ margin-bottom:20px; } } + /* Media References */ + .media__references { + margin-top: 20px; + } diff --git a/frontend/js/components/media-library/MediaSidebar.vue b/frontend/js/components/media-library/MediaSidebar.vue index 13ecb26ed..434c0a2a7 100755 --- a/frontend/js/components/media-library/MediaSidebar.vue +++ b/frontend/js/components/media-library/MediaSidebar.vue @@ -19,6 +19,13 @@ + + @@ -243,7 +250,8 @@ return this.extraMetadatas.filter(m => !m.multiple || (m.multiple && this.translatableMetadatas.includes(m.name))) }, ...mapState({ - mediasLoading: state => state.mediaLibrary.loading + mediasLoading: state => state.mediaLibrary.loading, + showMediaReferences: state => state.mediaLibrary.showMediaReferences }) }, methods: { diff --git a/frontend/js/store/modules/media-library.js b/frontend/js/store/modules/media-library.js index 30267fcf0..4667cb910 100644 --- a/frontend/js/store/modules/media-library.js +++ b/frontend/js/store/modules/media-library.js @@ -78,7 +78,12 @@ const state = { * An index used when mediaLibrary is open to replace a file * @type {number} */ - indexToReplace: -1 + indexToReplace: -1, + /** + * Displays where an image, file or record is being referenced + * @type {Boolean} + */ + showMediaReferences: window[process.env.VUE_APP_NAME].STORE.medias.showMediaReferences || false } // getters diff --git a/src/Models/File.php b/src/Models/File.php index 2f437c5aa..bcef2acc4 100755 --- a/src/Models/File.php +++ b/src/Models/File.php @@ -4,6 +4,9 @@ use FileService; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; +use Illuminate\Database\Eloquent\Relations\Relation; +use A17\Twill\Models\Behaviors\HasSlug; class File extends Model { @@ -40,6 +43,7 @@ public function toCmsArray() 'original' => FileService::getUrl($this->uuid), 'size' => $this->size, 'filesizeInMb' => number_format($this->attributes['size'] / 1048576, 2), + 'owners' => $this->getOwnerDetails(), ]; } @@ -47,4 +51,61 @@ public function getTable() { return config('twill.files_table', 'twill_files'); } + + public function getOwners() + { + $morphMap = Relation::morphMap(); + + $owners = collect( + DB::table(config('twill.fileables_table', 'twill_fileables')) + ->where('file_id', $this->id)->get() + ); + + return $owners->map(function ($owner) use ($morphMap){ + $resolvedClass = array_key_exists($owner->fileable_type, $morphMap) ? $morphMap[ $owner->fileable_type ] : $owner->fileable_type; + + return resolve($resolvedClass)::find($owner->fileable_id); + + }); + } + + public function getOwnerDetails() + { + $owners = $this->getOwners(); + + return collect(($owners))->filter(function ($value){ + return is_object($value); + })->map(function ($item){ + $module = Str::plural(lcfirst((new \ReflectionClass($item))->getShortName())); + + if ($item instanceof Block){ + $model=$item->blockable; + + $module = $model ? Str::plural(lcfirst((new \ReflectionClass($model))->getShortName())): null; + + return ($model && $module) ? [ + 'id' => $model->id, + 'slug' => classHasTrait($model, HasSlug::class) ? $model->slug : null, + 'name' => $model->{$model->titleKey}, + 'titleKey' => $model->titleKey, + 'model'=>$model, + 'module'=>$module, + 'edit' => moduleRoute($module, config('twill.block_editor.browser_route_prefixes.' . $module), 'edit', $model->id), + ] : []; + + } + + return [ + 'id' => $item->id, + 'slug' => classHasTrait($item, HasSlug::class) ? $item->slug : null, + 'name' => $item->{$item->titleKey}, + 'titleKey' => $item->titleKey, + 'model'=>$item, + 'module'=>$module, + 'edit' => moduleRoute($module, config('twill.block_editor.browser_route_prefixes.' . $module), 'edit', $item->id), + ]; + + })->filter()->values()->toArray(); + + } } diff --git a/src/Models/Media.php b/src/Models/Media.php index d36abe6cd..dc0ba295b 100755 --- a/src/Models/Media.php +++ b/src/Models/Media.php @@ -6,6 +6,8 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use ImageService; +use Illuminate\Database\Eloquent\Relations\Relation; +use A17\Twill\Models\Behaviors\HasSlug; class Media extends Model { @@ -92,6 +94,7 @@ public function toCmsArray() 'video' => null, ], ], + 'owners' => $this->getOwnerDetails(), ]; } @@ -133,4 +136,61 @@ public function getTable() { return config('twill.medias_table', 'twill_medias'); } + + public function getOwners() + { + $morphMap = Relation::morphMap(); + + $owners = collect( + DB::table(config('twill.mediables_table', 'twill_mediables')) + ->where('media_id', $this->id)->get() + ); + + return $owners->map(function ($owner) use ($morphMap){ + $resolvedClass = array_key_exists($owner->mediable_type, $morphMap) ? $morphMap[ $owner->mediable_type ] : $owner->mediable_type; + + return resolve($resolvedClass)::find($owner->mediable_id); + + }); + } + + public function getOwnerDetails() + { + $owners = $this->getOwners(); + + return collect(($owners))->filter(function ($value){ + return is_object($value); + })->map(function ($item){ + $module = Str::plural(lcfirst((new \ReflectionClass($item))->getShortName())); + + if ($item instanceof Block){ + $model=$item->blockable; + + $module = $model ? Str::plural(lcfirst((new \ReflectionClass($model))->getShortName())): null; + + return ($model && $module) ? [ + 'id' => $model->id, + 'slug' =>classHasTrait($model, HasSlug::class) ? $model->slug : null, + 'name' => $model->{$model->titleKey}, + 'titleKey' => $model->titleKey, + 'model'=>$model, + 'module'=>$module, + 'edit' => moduleRoute($module, config('twill.block_editor.browser_route_prefixes.' . $module), 'edit', $model->id), + ] : []; + + } + + return [ + 'id' => $item->id, + 'slug' => classHasTrait($item, HasSlug::class) ? $item->slug : null, + 'name' => $item->{$item->titleKey}, + 'titleKey' => $item->titleKey, + 'model'=>$item, + 'module'=>$module, + 'edit' => moduleRoute($module, config('twill.block_editor.browser_route_prefixes.' . $module), 'edit', $item->id), + ]; + + })->filter()->values()->toArray(); + + } } diff --git a/src/Models/Model.php b/src/Models/Model.php index c5bb72b59..c6b9f0753 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -17,6 +17,13 @@ abstract class Model extends BaseModel implements TaggableInterface public $timestamps = true; + /** + * Key of the index column to use as title column. + * + * @var string + */ + public $titleKey = 'title'; + public function scopePublished($query) { return $query->wherePublished(true); diff --git a/views/layouts/main.blade.php b/views/layouts/main.blade.php index f1312fb17..ccd06bc47 100644 --- a/views/layouts/main.blade.php +++ b/views/layouts/main.blade.php @@ -97,6 +97,7 @@ uploaderConfig: {!! json_encode($mediasUploaderConfig) !!} }); window['{{ config('twill.js_namespace') }}'].STORE.medias.showFileName = !!'{{ config('twill.media_library.show_file_name') }}'; + window['{{ config('twill.js_namespace') }}'].STORE.medias.showMediaReferences = !!'{{ config('twill.media_library.show_media_references') }}'; @endif @if (config('twill.enabled.file-library'))