diff --git a/include/ajax.forms.php b/include/ajax.forms.php index 070606298089eb337009a6203dcddd97dcf3d6c6..8044fafc32c7b1d9e77ee93f341d14903627d5f4 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -39,8 +39,10 @@ class DynamicFormsAjaxAPI extends AjaxController { function saveFieldConfiguration($field_id) { $field = DynamicFormField::lookup($field_id); - if (!$field->setConfiguration()) - return (include STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php'); + if (!$field->setConfiguration()) { + include STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php'; + return; + } else $field->save(); Http::response(201, 'Field successfully updated'); @@ -75,8 +77,10 @@ class DynamicFormsAjaxAPI extends AjaxController { if (!$list || !($item = $list->getItem( (int) $item_id))) Http::response(404, 'No such list item'); - if (!$item->setConfiguration()) - return (include STAFFINC_DIR . 'templates/list-item-properties.tmpl.php'); + if (!$item->setConfiguration()) { + include STAFFINC_DIR . 'templates/list-item-properties.tmpl.php'; + return; + } else $item->save(); diff --git a/include/class.forms.php b/include/class.forms.php index 68175ed43d2cf98ac47865a74a793d5db7c465d3..98ae957cf003094fafa797867d1e42352fc2c96f 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -1260,6 +1260,13 @@ class FileUploadField extends FormField { if ($next < $limit * 2) $sizes[$limit] = Format::file_size($limit); + // Load file types + $_types = YamlDataParser::load(INCLUDE_DIR . '/config/filetype.yaml'); + $types = array(); + foreach ($_types as $type=>$info) { + $types[$type] = $info['description']; + } + global $cfg; return array( 'size' => new ChoiceField(array( @@ -1268,11 +1275,16 @@ class FileUploadField extends FormField { 'default'=>$cfg->getMaxFileSize(), 'choices'=>$sizes )), + 'mimetypes' => new ChoiceField(array( + 'label'=>'Allowed File Types', + 'hint'=>'Choose file types to accept. To accept any file, leave all unchecked', + 'required'=>false, + 'choices'=>$types, + 'configuration'=>array('multiselect'=>true) + )), 'extensions' => new TextareaField(array( - 'label'=>'Allowed Extensions', - 'hint'=>'Enter allowed file extensions separated by a comma. - e.g .doc, .pdf. To accept all files enter wildcard - <b><i>.*</i></b> — i.e dotStar (NOT Recommended).', + 'label'=>'Allowed File Extensions', + 'hint'=>'Accept files based only on the file extension. Enter comma-separated list of extensions. (e.g .doc, .pdf).', 'default'=>$cfg->getAllowedFileTypes(), 'configuration'=>array('html'=>false, 'rows'=>2), )), @@ -1301,7 +1313,7 @@ class FileUploadField extends FormField { $file = array_shift($files); $file['name'] = urldecode($file['name']); - if (!$bypass && !$this->isValidFileType($file['name'])) + if (!$bypass && !$this->isValidFileType($file['name'], $file['type'])) Http::response(415, 'File type is not allowed'); $config = $this->getConfiguration(); @@ -1319,7 +1331,7 @@ class FileUploadField extends FormField { * for browsers which do not support the HTML5 way of uploading async. */ function uploadFile($file) { - if (!$this->isValidFileType($file['name'])) + if (!$this->isValidFileType($file['name'], $file['type'])) throw new FileUploadError(__('File type is not allowed')); $config = $this->getConfiguration(); @@ -1334,7 +1346,7 @@ class FileUploadField extends FormField { * sent other than via web upload */ function uploadAttachment(&$file) { - if (!$this->isValidFileType($file['name'])) + if (!$this->isValidFileType($file['name'], $file['type'])) throw new FileUploadError(__('File type is not allowed')); if (is_callable($file['data'])) @@ -1360,16 +1372,18 @@ class FileUploadField extends FormField { function isValidFileType($name, $type=false) { $config = $this->getConfiguration(); - // Return true if all file types are allowed (.*) - if (strpos($config['extensions'], '.*') || !$config['extensions']) + // Check MIME type - file ext. shouldn't be solely trusted. + if ($type && $config['__mimetypes'] + && in_array($type, $config['__mimetypes'])) return true; - $allowed = array_map('trim', explode(',', strtolower($config['extensions']))); + // Return true if all file types are allowed (.*) + if (strpos($config['__extensions'], '.*') || !$config['__extensions']) + return true; + $allowed = $config['__extensions']; $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); - //TODO: Check MIME type - file ext. shouldn't be solely trusted. - return ($ext && is_array($allowed) && in_array(".$ext", $allowed)); } @@ -1385,6 +1399,49 @@ class FileUploadField extends FormField { return $this->attachments ? $this->attachments->getAll() : array(); } + function getConfiguration() { + $config = parent::getConfiguration(); + $_types = YamlDataParser::load(INCLUDE_DIR . '/config/filetype.yaml'); + $mimetypes = array(); + $extensions = array(); + if (isset($config['mimetypes']) && is_array($config['mimetypes'])) { + foreach ($config['mimetypes'] as $type=>$desc) { + foreach ($_types[$type]['types'] as $mime=>$exts) { + $mimetypes[$mime] = true; + foreach ($exts as $ext) + $extensions['.'.$ext] = true; + } + } + } + if (strpos($config['extensions'], '.*') !== false) + $config['extensions'] = ''; + + foreach (preg_split('/\s+/', str_replace(',',' ', $config['extensions'])) as $ext) { + if (!$ext) { + continue; + } + elseif (strpos($ext, '/')) { + $mimetypes[$ext] = true; + } + else { + if ($ext[0] != '.') + $ext = '.' . $ext; + // Add this to the MIME types list so it can be exported to + // the @accept attribute + if (!isset($extensions[$ext])) + $mimetypes[$ext] = true; + + $extensions[$ext] = true; + } + } + $config['__extensions'] = array_keys($extensions); + + // 'mimetypes' is the array represented from the user interface, + // '__mimetypes' is a complete list of supported MIME types. + $config['__mimetypes'] = array_keys($mimetypes); + return $config; + } + // When the field is saved to database, encode the ID listing as a json // array. Then, inspect the difference between the files actually // attached to this field @@ -1835,7 +1892,8 @@ class FileUploadWidget extends Widget { <div class="dropzone"><i class="icon-upload"></i> Drop files here or <a href="#" class="manual">choose them</a> - <input type="file" class="multifile" multiple id="file-<?php echo $id; ?>" style="display:none;"/> + <input type="file" class="multifile" multiple id="file-<?php echo $id; ?>" style="display:none;" + accept="<?php echo implode(',', $config['__mimetypes']); ?>"/> </div></div> <script type="text/javascript"> $(function(){$('#<?php echo $id; ?> .dropzone').filedropbox({ @@ -1843,8 +1901,10 @@ class FileUploadWidget extends Widget { link: $('#<?php echo $id; ?>').find('a.manual'), paramname: 'upload[]', fallback_id: 'file-<?php echo $id; ?>', - allowedfileextensions: '<?php echo $config['extensions']; - ?>'.split(/,\s*/), + allowedfileextensions: <?php echo JsonDataEncoder::encode( + $config['__extensions']); ?>, + allowedfiletypes: <?php echo JsonDataEncoder::encode( + $config['__mimetypes']); ?>, maxfiles: <?php echo $config['max'] ?: 20; ?>, maxfilesize: <?php echo ($config['size'] ?: 1048576) / 1048576; ?>, name: '<?php echo $name; ?>[]', diff --git a/include/config/filetype.yaml b/include/config/filetype.yaml new file mode 100644 index 0000000000000000000000000000000000000000..dafd40109f0ece9aca82de1835373b36f652bed3 --- /dev/null +++ b/include/config/filetype.yaml @@ -0,0 +1,129 @@ +--- +image: + description: Images + types: + 'image/bmp': ['bmp'] + 'image/gif': ['gif'] + 'image/jpeg': ['jpeg', 'jpg'] + 'image/png': ['png'] + 'image/svg+xml': ['svg'] + 'image/tiff': ['tiff'] + 'image/vnd.adobe.photoshop': ['psd'] + 'image/vnd.microsoft.icon': ['ico'] + 'image/x-ico': ['ico'] + 'application/postscript': ['eps'] +audio: + description: Audio and Music + types: + 'audio/aiff': [] + 'audio/mpeg': ['mp3'] + 'audio/mp4': ['m4a', 'm4r', 'm4p'] + 'audio/ogg': ['ogg'] + 'audio/vorbis', + 'audio/vnd.wav': ['wav'] + 'audio/wav': ['wav'] + 'audio/x-midi': ['mid', 'midi'] +text: + description: Text Documents + types: + 'text/css': ['css'] + 'text/html': ['htm', 'html'] + 'text/javascript': ['js'] + 'text/plain': ['txt'] + 'text/xml': ['xml'] + 'application/json': ['json'] + 'application/javascript': ['js'] +office: + description: Common Office Documents + types: + # Microsoft Office + 'application/msword': ['doc'] + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['docx'] + 'application/vnd.ms-word.document.macroEnabled.12': ['docm'] + 'application/vnd.ms-excel': ['xls'] + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['xlsx'] + 'application/vnd.ms-excel.sheet.macroEnabled.12': ['xlsm'] + 'application/vnd.ms-excel.sheet.binary.macroEnabled.12': ['xlsb'] + 'application/vnd.ms-powerpoint': ['ppt'] + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['pptx'] + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow': ['ppsx'] + 'application/vnd.ms-powerpoint.presentation.macroEnabled.12': ['pptm'] + 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12': ['ppsm'] + 'application/vnd.ms-access': ['mdb', 'accdb'] + 'application/vnd.ms-project': [] + 'application/msonenote': [] + 'application/vnd.ms-publisher': [] + 'application/rtf': ['rtf'] + 'application/vnd.ms-works': [] + + # iWork + 'application/vnd.apple.keynote': ['keynote'] + 'application/vnd.apple.pages': ['pages'] + 'application/vnd.apple.numbers': ['numbers'] + + # OpenOffice + 'application/vnd.oasis.opendocument.text': [] + 'application/vnd.oasis.opendocument.text-web': [] + 'application/vnd.oasis.opendocument.text-master': [] + 'application/vnd.oasis.opendocument.graphics': [] + 'application/vnd.oasis.opendocument.presentation': [] + 'application/vnd.oasis.opendocument.spreadsheet': [] + 'application/vnd.oasis.opendocument.chart': [] + 'application/vnd.oasis.opendocument.formula': [] + 'application/vnd.oasis.opendocument.database': [] + 'application/vnd.oasis.opendocument.image': [] + 'application/vnd.openofficeorg.extension': [] + + # Other office + 'application/wordperfect': [] + 'application/vnd.kde.karbon': [] + 'application/vnd.kde.kchart': [] + 'application/vnd.kde.kformula': [] + 'application/vnd.kde.kivio': [] + 'application/vnd.kde.kontour': [] + 'application/vnd.kde.kpresenter': [] + 'application/vnd.kde.kspread': [] + 'application/vnd.kde.kword': [] + + # Creative / Common + 'application/pdf': ['pdf'] + '.csv': ['csv'] + 'application/illustrator': ['ai'] + 'application/x-director': [] + 'application/x-indesign': [] + + # Interchange + 'text/vcard': [] + + # Other + 'image/x-dwg': ['dwg'] + 'image/vnd.dwg': ['dwg'] + 'image/vnd.dxf': ['dxf'] + 'application/x-autocad': [] + 'application/x-mathcad': [] + 'application/x-msmoney': [] + + 'application/x-latex': ['tex'] +video: + description: Video Files + types: + 'video/avi': ['avi'] + 'video/mpeg': ['mpg','mpeg'] + 'video/mp4': ['mp4'] + 'video/ogg': ['ogg'] + 'video/quicktime': [] + 'video/webm': [] + 'video/x-ms-asf': [] + 'video/x-ms-wmv': [] + 'application/x-dvi': ['dvi'] + 'application/x-shockwave-flash': ['swf'] +archive: + description: Archives + types: + 'application/tar': ['tar'] + 'application/gzip': ['gz'] + 'application/x-lha': [] + 'application/rar': ['rar'] + 'application/x-compress': ['z'] + 'application/zip': ['zip'] + 'application/x-7z-compressed': ['7z'] diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index b160e2c253f9b993701ad18c7ad651bfcd88a358..e0bf6a4552b68b761ff0d78efb3fad9502acf4fa 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -277,17 +277,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </div> <script type="text/javascript"> -$(function() { - var $this = $('#popup-loading').hide(); - $(document).ajaxStart( function(event) { - console.log(1,event); - var $h1 = $this.find('h1'); - $this.show(); - $h1.css({'margin-top':$this.height()/3-$h1.height()/3}); // show Loading Div - }).ajaxStop ( function(){ - $this.hide(); // hide loading div - }); -}); $('form.manage-form').on('submit.inline', function(e) { var formObj = this, deleted = $('input.delete-box:checked', this); if (deleted.length) { diff --git a/scp/css/scp.css b/scp/css/scp.css index 27b80dfa0938eb7fdf3eaabb048f9269805d5a87..30c7dcb42c7ffea790b0ef27b0e963e75079ef88 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -1444,7 +1444,7 @@ time { } .dialog.draggable h3:hover { - cursor: crosshair; + cursor: move; } #advanced-search fieldset.span6 {