diff --git a/config/mimetypealiases.json b/config/mimetypealiases.json new file mode 100644 index 0000000000..d1684460ef --- /dev/null +++ b/config/mimetypealiases.json @@ -0,0 +1,68 @@ +{ + "_comment" : "When this file is changed make sure to run", + "_comment2": "./occ maintenance:mimetypesjs", + "_comment3": "Otherwise your update won't propagate through the system", + + + "application/coreldraw": "image", + "application/font-sfnt": "font", + "application/font-woff": "font", + "application/illustrator": "image/vector", + "application/json": "text/code", + "application/msaccess": "database", + "application/msexcel": "x-office/spreadsheet", + "application/mspowerpoint": "x-office/presentation", + "application/msword": "x-office/document", + "application/octet-stream": "file", + "application/postscript": "image/vector", + "application/vnd.android.package-archive": "package/x-generic", + "application/vnd.ms-excel": "x-office/spreadsheet", + "application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-fontobject": "font", + "application/vnd.ms-powerpoint": "x-office/presentation", + "application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-word.document.macroEnabled.12": "x-office/document", + "application/vnd.ms-word.template.macroEnabled.12": "x-office/document", + "application/vnd.oasis.opendocument.presentation": "x-office/presentation", + "application/vnd.oasis.opendocument.presentation-template": "x-office/presentation", + "application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet", + "application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet", + "application/vnd.oasis.opendocument.text": "x-office/document", + "application/vnd.oasis.opendocument.text-master": "x-office/document", + "application/vnd.oasis.opendocument.text-template": "x-office/document", + "application/vnd.oasis.opendocument.text-web": "x-office/document", + "application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet", + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document", + "application/x-7z-compressed": "package/x-generic", + "application/x-compressed": "package/x-generic", + "application/x-dcraw": "image", + "application/x-deb": "package/x-generic", + "application/x-font": "font", + "application/x-gimp": "image", + "application/x-gzip": "package/x-generic", + "application/x-perl": "text/code", + "application/x-photoshop": "image", + "application/x-php": "text/code", + "application/x-rar-compressed": "package/x-generic", + "application/x-tar": "package/x-generic", + "application/x-tex": "text", + "application/xml": "text/html", + "application/yaml": "text/code", + "application/zip": "package/x-generic", + "image/svg+xml": "image/vector", + "text/css": "text/code", + "text/csv": "x-office/spreadsheet", + "text/x-shellscript": "text/code" +} + diff --git a/core/command/maintenance/mimetypesjs.php b/core/command/maintenance/mimetypesjs.php new file mode 100644 index 0000000000..95a4bcd891 --- /dev/null +++ b/core/command/maintenance/mimetypesjs.php @@ -0,0 +1,112 @@ + + * + */ + +namespace OC\Core\Command\Maintenance; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class MimeTypesJS extends Command { + protected function configure() { + $this + ->setName('maintenance:mimetypesjs') + ->setDescription('Update mimetypelist.js'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + // Fetch all the aliases + $aliases = json_decode(file_get_contents(dirname(__DIR__) . '/../../config/mimetypealiases.json'), true); + + // Remove comments + $keys = array_filter(array_keys($aliases), function($k) { + return $k[0] === '_'; + }); + foreach($keys as $key) { + unset($aliases[$key]); + } + + // Fetch all files + $dir = new \DirectoryIterator(dirname(__DIR__) . '/../img/filetypes'); + + $files = []; + foreach($dir as $fileInfo) { + if ($fileInfo->isFile()) { + $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); + $files[] = $file; + } + } + + //Remove duplicates + $files = array_values(array_unique($files)); + + + // Fetch all themes! + $themes = []; + $dirs = new \DirectoryIterator(dirname(__DIR__) . '/../../themes/'); + foreach($dirs as $dir) { + //Valid theme dir + if ($dir->isFile() || $dir->isDot()) { + continue; + } + + $theme = $dir->getFilename(); + $themeDir = $dir->getPath() . '/' . $theme . '/core/img/filetypes/'; + // Check if this theme has its own filetype icons + if (!file_exists($themeDir)) { + continue; + } + + $themes[$theme] = []; + // Fetch all the theme icons! + $themeIt = new \DirectoryIterator($themeDir); + foreach ($themeIt as $fileInfo) { + if ($fileInfo->isFile()) { + $file = preg_replace('/.[^.]*$/', '', $fileInfo->getFilename()); + $themes[$theme][] = $file; + } + } + + //Remove Duplicates + $themes[$theme] = array_values(array_unique($themes[$theme])); + } + + //Generate the JS + $js = '/** +* This file is automatically generated +* DO NOT EDIT MANUALLY! +* +* You can update the list of MimeType Aliases in config/mimetypealiases.json +* The list of files is fetched from core/img/filetypes +* To regenerate this file run ./occ maintenance:mimetypesjs +*/ +OC.MimeTypeList={ + aliases: ' . json_encode($aliases, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . ', + files: ' . json_encode($files, JSON_PRETTY_PRINT) . ', + themes: ' . json_encode($themes, JSON_PRETTY_PRINT) . ' +}; +'; + + //Output the JS + file_put_contents(dirname(__DIR__) . '/../js/mimetypelist.js', $js); + + $output->writeln('mimetypelist.js is updated'); + } +} diff --git a/core/js/config.php b/core/js/config.php index d6946f671d..cecbf27e4b 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -128,7 +128,8 @@ $array = array( 'slogan' => $defaults->getSlogan(), 'logoClaim' => $defaults->getLogoClaim(), 'shortFooter' => $defaults->getShortFooter(), - 'longFooter' => $defaults->getLongFooter() + 'longFooter' => $defaults->getLongFooter(), + 'folder' => OC_Util::getTheme(), ) ) ); diff --git a/core/js/core.json b/core/js/core.json index 90bc318b23..e75db53455 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -27,6 +27,8 @@ "multiselect.js", "oc-requesttoken.js", "setupchecks.js", - "../search/js/search.js" + "../search/js/search.js", + "mimetype.js", + "mimetypelist.js" ] } diff --git a/core/js/mimetype.js b/core/js/mimetype.js new file mode 100644 index 0000000000..d22b0a2378 --- /dev/null +++ b/core/js/mimetype.js @@ -0,0 +1,113 @@ +/** + * @author Roeland Jago Douma + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +/** + * Namespace to hold functions related to convert mimetype to icons + * + * @namespace + */ +OC.MimeType = { + + /** + * Cache that maps mimeTypes to icon urls + */ + _mimeTypeIcons: {}, + + /** + * Return the file icon we want to use for the given mimeType. + * The file needs to be present in the supplied file list + * + * @param {string} mimeType The mimeType we want an icon for + * @param {array} files The available icons in this theme + * @return {string} The icon to use or null if there is no match + */ + _getFile: function(mimeType, files) { + var icon = mimeType.replace(new RegExp('/', 'g'), '-'); + + // Generate path + if (mimeType === 'dir' && $.inArray('folder', files) !== -1) { + return 'folder'; + } else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) { + return 'folder-shared'; + } else if (mimeType === 'dir-external' && $.inArray('folder-external', files) !== -1) { + return 'folder-external'; + } else if ($.inArray(icon, files) !== -1) { + return icon; + } else if ($.inArray(icon.split('-')[0], files) !== -1) { + return icon.split('-')[0]; + } else if ($.inArray('file', files) !== -1) { + return 'file'; + } + + return null; + }, + + /** + * Return the url to icon of the given mimeType + * + * @param {string} mimeType The mimeType to get the icon for + * @return {string} Url to the icon for mimeType + */ + getIconUrl: function(mimeType) { + if (_.isUndefined(mimeType)) { + return undefined; + } + + if (mimeType in OC.MimeTypeList.aliases) { + mimeType = OC.MimeTypeList.aliases[mimeType]; + } + if (mimeType in OC.MimeType._mimeTypeIcons) { + return OC.MimeType._mimeTypeIcons[mimeType]; + } + + // First try to get the correct icon from the current theme + var gotIcon = null; + var path = ''; + if (OC.theme.folder !== '' && $.isArray(OC.MimeTypeList.themes[OC.theme.folder])) { + path = OC.webroot + '/themes/' + OC.theme.folder + '/core/img/filetypes/'; + var icon = OC.MimeType._getFile(mimeType, OC.MimeTypeList.themes[OC.theme.folder]); + + if (icon !== null) { + gotIcon = true; + path += icon; + } + } + + // If we do not yet have an icon fall back to the default + if (gotIcon === null) { + path = OC.webroot + '/core/img/filetypes/'; + path += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files); + } + + // Use svg if we can + if(OC.Util.hasSVGSupport()){ + path += '.svg'; + } else { + path += '.png'; + } + + // Cache the result + OC.MimeType._mimeTypeIcons[mimeType] = path; + return path; + } + +}; + + diff --git a/core/js/mimetypelist.js b/core/js/mimetypelist.js new file mode 100644 index 0000000000..2a780fc2bc --- /dev/null +++ b/core/js/mimetypelist.js @@ -0,0 +1,107 @@ +/** +* This file is automatically generated +* DO NOT EDIT MANUALLY! +* +* You can update the list of MimeType Aliases in config/mimetypealiases.json +* The list of files is fetched from core/img/filetypes +* To regenerate this file run ./occ maintenance:mimetypesjs +*/ +OC.MimeTypeList={ + aliases: { + "application/coreldraw": "image", + "application/font-sfnt": "font", + "application/font-woff": "font", + "application/illustrator": "image/vector", + "application/json": "text/code", + "application/msaccess": "database", + "application/msexcel": "x-office/spreadsheet", + "application/mspowerpoint": "x-office/presentation", + "application/msword": "x-office/document", + "application/octet-stream": "file", + "application/postscript": "image/vector", + "application/vnd.android.package-archive": "package/x-generic", + "application/vnd.ms-excel": "x-office/spreadsheet", + "application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet", + "application/vnd.ms-fontobject": "font", + "application/vnd.ms-powerpoint": "x-office/presentation", + "application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation", + "application/vnd.ms-word.document.macroEnabled.12": "x-office/document", + "application/vnd.ms-word.template.macroEnabled.12": "x-office/document", + "application/vnd.oasis.opendocument.presentation": "x-office/presentation", + "application/vnd.oasis.opendocument.presentation-template": "x-office/presentation", + "application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet", + "application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet", + "application/vnd.oasis.opendocument.text": "x-office/document", + "application/vnd.oasis.opendocument.text-master": "x-office/document", + "application/vnd.oasis.opendocument.text-template": "x-office/document", + "application/vnd.oasis.opendocument.text-web": "x-office/document", + "application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet", + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document", + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document", + "application/x-7z-compressed": "package/x-generic", + "application/x-compressed": "package/x-generic", + "application/x-dcraw": "image", + "application/x-deb": "package/x-generic", + "application/x-font": "font", + "application/x-gimp": "image", + "application/x-gzip": "package/x-generic", + "application/x-perl": "text/code", + "application/x-photoshop": "image", + "application/x-php": "text/code", + "application/x-rar-compressed": "package/x-generic", + "application/x-tar": "package/x-generic", + "application/x-tex": "text", + "application/xml": "text/html", + "application/yaml": "text/code", + "application/zip": "package/x-generic", + "image/svg+xml": "image/vector", + "text/css": "text/code", + "text/csv": "x-office/spreadsheet", + "text/x-shellscript": "text/code" +}, + files: [ + "text-x-h", + "application-rss+xml", + "video", + "folder-drag-accept", + "application-epub+zip", + "folder-public", + "package-x-generic", + "application-x-shockwave-flash", + "text", + "folder-external", + "web", + "text-vcard", + "application", + "image-vector", + "database", + "text-code", + "text-x-python", + "x-office-spreadsheet", + "application-pdf", + "folder", + "x-office-document", + "text-html", + "text-calendar", + "x-office-presentation", + "text-x-c", + "file", + "font", + "folder-shared", + "application-x-cbr", + "application-javascript", + "image", + "audio" +], + themes: [] +}; diff --git a/core/js/tests/specs/mimeTypeSpec.js b/core/js/tests/specs/mimeTypeSpec.js new file mode 100644 index 0000000000..182941de1a --- /dev/null +++ b/core/js/tests/specs/mimeTypeSpec.js @@ -0,0 +1,151 @@ +/** + * @author Roeland Jago Douma + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +describe('MimeType tests', function() { + var _files; + var _aliases; + var _theme; + + beforeEach(function() { + _files = OC.MimeTypeList.files; + _aliases = OC.MimeTypeList.aliases; + _theme = OC.MimeTypeList.themes['abc']; + + OC.MimeTypeList.files = ['folder', 'folder-shared', 'folder-external', 'foo-bar', 'foo', 'file']; + OC.MimeTypeList.aliases = {'app/foobar': 'foo/bar'}; + OC.MimeTypeList.themes['abc'] = ['folder']; + }); + + afterEach(function() { + OC.MimeTypeList.files = _files; + OC.MimeTypeList.aliases = _aliases; + OC.MimeTypeList.themes['abc'] = _theme; + }); + + describe('_getFile', function() { + + it('returns the correct icon for "dir"', function() { + var res = OC.MimeType._getFile('dir', OC.MimeTypeList.files); + expect(res).toEqual('folder'); + }); + + it('returns the correct icon for "dir-shared"', function() { + var res = OC.MimeType._getFile('dir-shared', OC.MimeTypeList.files); + expect(res).toEqual('folder-shared'); + }); + + it('returns the correct icon for "dir-external"', function() { + var res = OC.MimeType._getFile('dir-external', OC.MimeTypeList.files); + expect(res).toEqual('folder-external'); + }); + + it('returns the correct icon for a mimetype for which we have an icon', function() { + var res = OC.MimeType._getFile('foo/bar', OC.MimeTypeList.files); + expect(res).toEqual('foo-bar'); + }); + + it('returns the correct icon for a mimetype for which we only have a general mimetype icon', function() { + var res = OC.MimeType._getFile('foo/baz', OC.MimeTypeList.files); + expect(res).toEqual('foo'); + }); + + it('return the file mimetype if we have no matching icon but do have a file icon', function() { + var res = OC.MimeType._getFile('foobar', OC.MimeTypeList.files); + expect(res).toEqual('file'); + }); + + it('return null if we do not have a matching icon', function() { + var res = OC.MimeType._getFile('xyz', []); + expect(res).toEqual(null); + }); + }); + + describe('getIconUrl', function() { + + describe('no theme', function() { + var _themeFolder; + + beforeEach(function() { + _themeFolder = OC.theme.folder; + OC.theme.folder = ''; + //Clear mimetypeIcons caches + OC.MimeType._mimeTypeIcons = {}; + }); + + afterEach(function() { + OC.theme.folder = _themeFolder; + }); + + it('return undefined if the an icon for undefined is requested', function() { + var res = OC.MimeType.getIconUrl(undefined); + expect(res).toEqual(undefined); + }); + + it('return the url for the mimetype file', function() { + var res = OC.MimeType.getIconUrl('file'); + expect(res).toEqual(OC.webroot + '/core/img/filetypes/file.svg'); + }); + + it('test if the cache works correctly', function() { + OC.MimeType._mimeTypeIcons = {}; + expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(0); + + var res = OC.MimeType.getIconUrl('dir'); + expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(1); + expect(OC.MimeType._mimeTypeIcons['dir']).toEqual(res); + + var res = OC.MimeType.getIconUrl('dir-shared'); + expect(Object.keys(OC.MimeType._mimeTypeIcons).length).toEqual(2); + expect(OC.MimeType._mimeTypeIcons['dir-shared']).toEqual(res); + }); + + it('test if alaiases are converted correctly', function() { + var res = OC.MimeType.getIconUrl('app/foobar'); + expect(res).toEqual(OC.webroot + '/core/img/filetypes/foo-bar.svg'); + expect(OC.MimeType._mimeTypeIcons['foo/bar']).toEqual(res); + }); + }); + + describe('themes', function() { + var _themeFolder; + + beforeEach(function() { + _themeFolder = OC.theme.folder; + OC.theme.folder = 'abc'; + //Clear mimetypeIcons caches + OC.MimeType._mimeTypeIcons = {}; + }); + + afterEach(function() { + OC.theme.folder = _themeFolder; + }); + + it('test if theme path is used if a theme icon is availble', function() { + var res = OC.MimeType.getIconUrl('dir'); + expect(res).toEqual(OC.webroot + '/themes/abc/core/img/filetypes/folder.svg'); + }); + + it('test if we fallback to the default theme if no icon is available in the theme', function() { + var res = OC.MimeType.getIconUrl('dir-shared'); + expect(res).toEqual(OC.webroot + '/core/img/filetypes/folder-shared.svg'); + }); + }); + }); +}); diff --git a/core/register_command.php b/core/register_command.php index 03775fd787..13010b93a9 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -56,6 +56,7 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\Encryption\ListModules(\OC::$server->getEncryptionManager())); $application->add(new OC\Core\Command\Encryption\SetDefaultModule(\OC::$server->getEncryptionManager())); $application->add(new OC\Core\Command\Encryption\Status(\OC::$server->getEncryptionManager())); + $application->add(new OC\Core\Command\Maintenance\MimeTypesJS()); } else { $application->add(new OC\Core\Command\Maintenance\Install(\OC::$server->getConfig())); } diff --git a/lib/base.php b/lib/base.php index 1766fc38f8..9a682a7e90 100644 --- a/lib/base.php +++ b/lib/base.php @@ -406,6 +406,8 @@ class OC { OC_Util::addScript('search', 'search'); OC_Util::addScript("oc-requesttoken"); OC_Util::addScript("apps"); + OC_Util::addScript('mimetype'); + OC_Util::addScript('mimetypelist'); OC_Util::addVendorScript('snapjs/dist/latest/snap'); // avatars