admin-addon-media-metadata.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <?php
  2. namespace Grav\Plugin;
  3. use Composer\Autoload\ClassLoader;
  4. use Grav\Common\Page\Media;
  5. use Grav\Common\Plugin;
  6. use Grav\Common\Yaml;
  7. use RocketTheme\Toolbox\File\File;
  8. /**
  9. * Class AdminAddonMediaMetadataPlugin
  10. * some of the code is based on the Admin Addon Media Rename by Dávid Szabó
  11. * see https://github.com/david-szabo97/grav-plugin-admin-addon-media-rename
  12. * @package Grav\Plugin
  13. */
  14. class AdminAddonMediaMetadataPlugin extends Plugin
  15. {
  16. const ROUTE = '/admin-addon-media-metadata';
  17. const TASK_METADATA = 'AdminAddonMediaMetadataEdit';
  18. /**
  19. * @return array
  20. *
  21. * The getSubscribedEvents() gives the core a list of events
  22. * that the plugin wants to listen to. The key of each
  23. * array section is the event that the plugin listens to
  24. * and the value (in the form of an array) contains the
  25. * callable (or function) as well as the priority. The
  26. * higher the number the higher the priority.
  27. */
  28. public static function getSubscribedEvents()
  29. {
  30. return [
  31. ['autoload', 100000], // TODO: Remove when plugin requires Grav >=1.7
  32. 'onPluginsInitialized' => ['onPluginsInitialized', 0]
  33. ];
  34. }
  35. /**
  36. * Composer autoload.
  37. * is
  38. * @return ClassLoader
  39. */
  40. public function autoload(): ClassLoader
  41. {
  42. return require __DIR__ . '/vendor/autoload.php';
  43. }
  44. public function getPath()
  45. {
  46. return '/' . trim($this->grav['admin']->base, '/') . '/' . trim(self::ROUTE, '/');
  47. }
  48. public function buildBaseUrl()
  49. {
  50. return rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->getPath(), '/');
  51. }
  52. /**
  53. * Initialize the plugin
  54. */
  55. public function onPluginsInitialized()
  56. {
  57. if (!$this->isAdmin() || !$this->grav['user']->authenticated) {
  58. return;
  59. }
  60. if ($this->grav['uri']->path() == $this->getPath()) {
  61. $this->enable([
  62. 'onPagesInitialized' => ['processRenameRequest', 0]
  63. ]);
  64. return;
  65. }
  66. $this->enable([
  67. 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
  68. 'onPagesInitialized' => ['onTwigExtensions', 0],
  69. //'onAdminAfterAddMedia' => ['createMetaYaml', 0], // removing the call of this method: see method below
  70. 'onAdminTaskExecute' => ['editMetaDataFile', 0],
  71. ]);
  72. }
  73. public function onTwigTemplatePaths()
  74. {
  75. $this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
  76. }
  77. public function onTwigExtensions()
  78. {
  79. $page = $this->grav['admin']->page(true);
  80. if (!$page) {
  81. return;
  82. }
  83. /**
  84. * get metadata form field config from plugin admin-addon-media-metadata.yaml file
  85. * or local override in user/config/plugins/admin-addon-media-metadata.yaml
  86. * or from page frontmatter
  87. */
  88. //$formFields = $this->config->get('plugins.admin-addon-media-metadata.metadata_form');
  89. $config = $this->mergeConfig($page, true);
  90. $formFields = $config->get('metadata_form');
  91. /**
  92. * list all needed data keys from the fields configuration
  93. */
  94. $arrMetaKeys = $this->editableFields($formFields['fields']);
  95. $path = $page->path();
  96. $media = new Media($path);
  97. $allMedia = $media->all();
  98. $arrFiles = [];
  99. $i = 0;
  100. foreach ($allMedia as $filename => $file) {
  101. $metadata = $file->meta();
  102. $arrFiles[$filename] = [
  103. 'filename' => $filename,
  104. ];
  105. /**
  106. * for each file: write stored metadata for each editable field into an array
  107. * this will be output as inline JS variable adminAddonMediaMetadata
  108. */
  109. foreach ($arrMetaKeys as $metaKey => $info) {
  110. $arrFiles[$filename][$metaKey] = $metadata->$metaKey;
  111. }
  112. $i++;
  113. }
  114. $jsArrFormFields = '';
  115. $i = 0;
  116. foreach ($arrMetaKeys as $metaKey => $info) {
  117. $jsArrFormFields .= ($i > 0) ? ",'" . $metaKey . "'" : "'" . $metaKey . "'";
  118. $i++;
  119. }
  120. $inlineJs = 'var metadataFormFields = [' . $jsArrFormFields . '];';
  121. $inlineJs .= PHP_EOL . 'var mediaListOnLoad = ' . json_encode($arrFiles) . ';';
  122. $modal = $this->grav['twig']->twig()->render('metadata-modal.html.twig', $formFields);
  123. $jsConfig = [
  124. 'PATH' => $this->buildBaseUrl() . '/' . $page->route() . '/task:' . self::TASK_METADATA,
  125. 'MODAL' => $modal
  126. ];
  127. $inlineJs .= PHP_EOL . 'var adminAddonMediaMetadata = ' . json_encode($jsConfig) . ';';
  128. $inlineJs .= PHP_EOL . $this->grav['twig']->twig()->render('additionalInlineJS.html.twig');
  129. $this->grav['assets']->addInlineJs($inlineJs, -1000);
  130. $this->grav['assets']->addCss('plugin://admin-addon-media-metadata/admin-addon-media-metadata.css', -1000);
  131. $this->grav['assets']->addJs('plugin://admin-addon-media-metadata/admin-addon-media-metadata.js', -1000);
  132. }
  133. public function editTest()
  134. {
  135. $this->outputError('blob');
  136. }
  137. /**
  138. * writes metadata into the [mediafile].meta.yaml file
  139. * creates the .meta.yaml file if it does not yet exist
  140. */
  141. public function editMetaDataFile($e)
  142. {
  143. $method = $e['method'];
  144. if ($method === 'task' . self::TASK_METADATA) {
  145. $fileName = $_POST['filename'];
  146. $pageObj = $this->grav['admin']->page();
  147. $basePath = $pageObj->path() . DS;
  148. $filePath = $basePath . $fileName;
  149. /**
  150. * temporarily removing the condition that checks for the media file to exist
  151. * as in Admin plugin 1.10 a media file will be uploaded to a tmp folder first
  152. * and moved to the page folder on saving the page
  153. *
  154. * there needs to be a better solution for this
  155. *
  156. * also: take care of system.yaml → media.auto_metadata_exif
  157. * if set to TRUE, a meta.yaml with EXIF data will be written, but only if the meta.yaml does not yet exist
  158. * in Admin 1.10 you need to save a page in order to have the meta.yaml file with EXIF data created
  159. * in Admin 1.9 this is not a problem since this plugin now (>=1.1.0) writes metadata only when needed
  160. */
  161. // if (!file_exists($filePath)) {
  162. // $this->outputError($this->grav['language']->translate(['PLUGIN_ADMIN_ADDON_MEDIA_METADATA.ERRORS.MEDIA_FILE_NOT_FOUND', $filePath]));
  163. // } else {
  164. $metaDataFilePath = $filePath . '.meta.yaml';
  165. /**
  166. * get the list of form data from the fields configuration
  167. */
  168. $arrMetaKeys = $this->editableFields();
  169. if (file_exists($metaDataFilePath)) {
  170. /**
  171. * get array of all current metadata for the file
  172. * this is to avoid overwriting data that has been added to the meta.yaml file in the file browser
  173. */
  174. $storedMetaData = Yaml::parse(file_get_contents($metaDataFilePath));
  175. /**
  176. * overwrite the currently stored data for each field in the form
  177. */
  178. foreach ($arrMetaKeys as $metaKey => $info) {
  179. if (isset($_POST[$metaKey])) {
  180. $storedMetaData[$metaKey] = $_POST[$metaKey];
  181. }
  182. }
  183. } else {
  184. /**
  185. * create metaData in case the meta data file does not yet exist
  186. */
  187. $storedMetaData = [];
  188. foreach ($arrMetaKeys as $metaKey => $info) {
  189. if (isset($_POST[$metaKey])) {
  190. $storedMetaData[$metaKey] = $_POST[$metaKey];
  191. } else {
  192. $storedMetaData[$metaKey] = '';
  193. }
  194. }
  195. }
  196. /**
  197. * Get an instance of the meta file and write the data to it
  198. * @see \Grav\Common\Page\Media
  199. */
  200. $metaDataFile = File::instance($metaDataFilePath);
  201. $metaDataFile->save(Yaml::dump($storedMetaData));
  202. //$this->outputError($newYamlText);
  203. // }
  204. }
  205. }
  206. /**
  207. * this method will not be called in Admin Plugin v1.10 (up until rc.11 anyway)
  208. * additionally, writing the YAML file on upload will meddle with the system functionality
  209. * that writes exif data
  210. * in Admin Plugin v1.9 this functionality writes the meta.yaml file
  211. * on upload, this plugin will then just add the other metadata
  212. * when writing the meta.yaml file upon upload the system functionality will be overwritten
  213. * in Admin Plugin v1.10 you need to save the page before using this plugin on a file
  214. * if you want the exif data to be stored
  215. */
  216. /**
  217. * creates an image.meta.yaml file
  218. * this file will be deleted by the core when deleting an image
  219. * will be called after a media file has been added (see onPluginsInitialized())
  220. */
  221. // public function createMetaYaml()
  222. // {
  223. // $fileName = $_FILES['file']['name'];
  224. //
  225. // $pageObj = $this->grav['admin']->page();
  226. // $basePath = $pageObj->path() . DS;
  227. //
  228. // $filePath = $basePath . $fileName;
  229. // if (!file_exists($filePath)) {
  230. // $this->outputError($this->grav['language']->translate(['PLUGIN_ADMIN_ADDON_MEDIA_METADATA.ERRORS.MEDIA_FILE_NOT_FOUND', $filePath]));
  231. // } else {
  232. // // TODO: do that only for image files?
  233. // $metaDataFileName = $fileName . '.meta.yaml';
  234. // $metaDataFilePath = $basePath . $metaDataFileName;
  235. // if (!file_exists($metaDataFilePath)) {
  236. // /**
  237. // * get the list of form data from the fields configuration
  238. // */
  239. // $arrMetaKeys = $this->editableFields();
  240. //
  241. // $newMetaData = [];
  242. // foreach ($arrMetaKeys as $metaKey => $info) {
  243. // $newMetaData[$metaKey] = '';
  244. // }
  245. //
  246. // /**
  247. // * Get an instance of the meta file and write the data to it
  248. // * @see \Grav\Common\Page\Media
  249. // */
  250. // $metaDataFile = File::instance($metaDataFilePath);
  251. // $metaDataFile->save(Yaml::dump($newMetaData));
  252. // }
  253. // }
  254. // }
  255. /**
  256. * return all editable fields from form configuration
  257. */
  258. private function editableFields($fieldsConf = null)
  259. {
  260. if ($fieldsConf === null) {
  261. $page = $this->grav['admin']->page(true);
  262. if (!$page) {
  263. return;
  264. }
  265. /**
  266. * get metadata form field config from plugin admin-addon-media-metadata.yaml file
  267. * or local override in user/config/plugins/admin-addon-media-metadata.yaml
  268. * or from page frontmatter
  269. */
  270. //$formFields = $this->config->get('plugins.admin-addon-media-metadata.metadata_form');
  271. $config = $this->mergeConfig($page, true);
  272. $formFields = $config->get('metadata_form');
  273. $fieldsConf = $formFields['fields'];
  274. }
  275. $arrMetaKeys = [];
  276. foreach ($fieldsConf as $singleFieldConf) {
  277. if (isset($singleFieldConf['name'], $singleFieldConf['type']) && $singleFieldConf['name'] !== 'filename') {
  278. $arrMetaKeys[$singleFieldConf['name']] = [
  279. 'name' => $singleFieldConf['name'],
  280. 'type' => $singleFieldConf['type']
  281. ];
  282. }
  283. }
  284. return $arrMetaKeys;
  285. }
  286. public function outputError($msg)
  287. {
  288. header('HTTP/1.1 400 Bad Request');
  289. die(json_encode(['error' => ['msg' => $msg]]));
  290. }
  291. }