private function cleanFilesData($key, $file) { $config = $this->grav['config']; $blueprint = isset($this->items['fields'][$key]['files']) ? $this->items['fields'][$key]['files'] : []; /** @var Page $page */ $page = null; $cleanFiles[$key] = []; if (!isset($blueprint)) { return false; } $type = trim("{$this->view}/{$this->admin->route}", '/'); $data = $this->admin->data($type, $this->post); $fields = $data->blueprints()->fields(); $blueprint = isset($fields[$key]) ? $fields[$key] : []; $cleanFiles = [$key => []]; foreach ((array) $file['error'] as $index => $error) { if ($error == UPLOAD_ERR_OK) { $tmp_name = $file['tmp_name'][$index]; $name = $file['name'][$index]; $type = $file['type'][$index]; $destination = Folder::getRelativePath(rtrim($blueprint['destination'], '/')); if (!$this->match_in_array($type, $blueprint['accept'])) { throw new \RuntimeException('File "' . $name . '" is not an accepted MIME type.'); } if (Utils::startsWith($destination, '@page:')) { $parts = explode(':', $destination); $route = $parts[1]; $page = $this->grav['page']->find($route); if (!$page) { throw new \RuntimeException('Unable to upload file to destination. Page route not found.'); } $destination = $page->relativePagePath(); } else { if ($destination == '@self') { $page = $this->admin->page(true); $destination = $page->relativePagePath(); } else { Folder::mkdir($destination); } } if (move_uploaded_file($tmp_name, "{$destination}/{$name}")) { $path = $page ? $this->grav['uri']->convertUrl($page, $page->route() . '/' . $name) : $destination . '/' . $name; $cleanFiles[$key][] = $path; } else { throw new \RuntimeException("Unable to upload file(s) to {$destination}/{$name}"); } } } return $cleanFiles[$key]; }
/** * Detects all plugins with a configuration file and returns last modification time. * * @param string $lookup Location to look up from. * @param bool $blueprints * @return array * @internal */ protected function detectConfig($lookup = SYSTEM_DIR, $blueprints = false) { $location = $blueprints ? 'blueprintFiles' : 'configFiles'; $path = trim(Folder::getRelativePath($lookup), '/'); if (isset($this->{$location}[$path])) { return [$path => $this->{$location}[$path]]; } if (is_dir($lookup)) { // Find all system and user configuration files. $options = ['compare' => 'Filename', 'pattern' => '|\\.yaml$|', 'filters' => ['key' => '|\\.yaml$|', 'value' => function (\RecursiveDirectoryIterator $file) use($path) { return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]; }], 'key' => 'SubPathname']; $list = Folder::all($lookup, $options); } else { $list = []; } $this->{$location}[$path] = $list; return [$path => $list]; }
private function cleanFilesData($key, $file) { /** @var Page $page */ $page = null; $blueprint = $this->items['fields'][$key]['files']; $cleanFiles[$key] = []; if (!isset($blueprint)) { return false; } $cleanFiles = [$key => []]; foreach ((array) $file['error'] as $index => $error) { if ($error == UPLOAD_ERR_OK) { $tmp_name = $file['tmp_name'][$index]; $name = $file['name'][$index]; $type = $file['type'][$index]; $destination = Folder::getRelativePath(rtrim($blueprint['destination'], '/')); if (!$this->match_in_array($type, $blueprint['accept'])) { throw new \RuntimeException('File "' . $name . '" is not an accepted MIME type.'); } if (Utils::startsWith($destination, '@page:')) { $parts = explode(':', $destination); $route = $parts[1]; $page = self::getGrav()['page']->find($route); if (!$page) { throw new \RuntimeException('Unable to upload file to destination. Page route not found.'); } $destination = $page->relativePagePath(); } else { if ($destination == '@self') { $page = self::getGrav()['page']; $destination = $page->relativePagePath(); } else { Folder::mkdir($destination); } } if (move_uploaded_file($tmp_name, "{$destination}/{$name}")) { $path = $page ? self::getGrav()['uri']->convertUrl($page, $page->route() . '/' . $name) : $destination . '/' . $name; $cleanFiles[$key][$path] = ['name' => $file['name'][$index], 'type' => $file['type'][$index], 'size' => $file['size'][$index], 'file' => $destination . '/' . $name, 'route' => $page ? $path : null]; } else { throw new \RuntimeException('Unable to upload file(s). Error Code: ' . $error); } } } return $cleanFiles[$key]; }
/** * Detects all plugins with a configuration file and returns them with last modification time. * * @param string $folder Location to look up from. * @param string $pattern Pattern to match the file. Pattern will also be removed from the key. * @param int $levels Maximum number of recursive directories. * @return array * @internal */ protected function detectAll($folder, $pattern, $levels) { $path = trim(Folder::getRelativePath($folder), '/'); if (is_dir($folder)) { // Find all system and user configuration files. $options = ['levels' => $levels, 'compare' => 'Filename', 'pattern' => $pattern, 'filters' => ['pre-key' => $this->base, 'key' => $pattern, 'value' => function (\RecursiveDirectoryIterator $file) use($path) { return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()]; }], 'key' => 'SubPathname']; $list = Folder::all($folder, $options); ksort($list); } else { $list = []; } return $list; }
/** * Detects all plugins with a configuration file and returns them with last modification time. * * @param string $folder Location to look up from. * @return array * @internal */ protected function detectRecursive($folder) { $path = trim(Folder::getRelativePath($folder), '/'); if (is_dir($folder)) { // Find all system and user configuration files. $options = ['compare' => 'Filename', 'pattern' => '|\\.yaml$|', 'filters' => ['key' => '|\\.yaml$|', 'value' => function (\RecursiveDirectoryIterator $file) use($path) { return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()]; }], 'key' => 'SubPathname']; $list = Folder::all($folder, $options); } else { $list = []; } return [$path => $list]; }
<?php /** * Multisite setup for sub-directories or path based * URLs for subsites. * * DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING! */ use Grav\Common\Filesystem\Folder; // Get relative path from Grav root. $path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : Folder::getRelativePath($_SERVER['REQUEST_URI'], ROOT_DIR); // Extract name of subsite from path $name = Folder::shift($path); $folder = "sites/{$name}"; $prefix = "/{$name}"; if (!$name || !is_dir(ROOT_DIR . "user/{$folder}")) { return []; } // Prefix all pages with the name of the subsite $container['pages']->base($prefix); return ['environment' => $name, 'streams' => ['schemes' => ['user' => ['type' => 'ReadOnlyStream', 'prefixes' => ['' => ["user/{$folder}"]]]]]];
/** * Handles ajax upload for files. * Stores in a flash object the temporary file and deals with potential file errors. * * @return mixed True if the action was performed. */ public function uploadFiles() { $post = $_POST; $grav = Grav::instance(); $uri = $grav['uri']->url; $config = $grav['config']; $session = $grav['session']; $settings = $this->data->blueprints()->schema()->getProperty($post['name']); $settings = (object) array_merge(['destination' => $config->get('plugins.form.files.destination', 'self@'), 'avoid_overwriting' => $config->get('plugins.form.files.avoid_overwriting', false), 'random_name' => $config->get('plugins.form.files.random_name', false), 'accept' => $config->get('plugins.form.files.accept', ['image/*']), 'limit' => $config->get('plugins.form.files.limit', 10), 'filesize' => $config->get('plugins.form.files.filesize', 5242880)], (array) $settings, ['name' => $post['name']]); $upload = $this->normalizeFiles($_FILES['data'], $settings->name); // Handle errors and breaks without proceeding further if ($upload->file->error != UPLOAD_ERR_OK) { // json_response return ['status' => 'error', 'message' => sprintf($grav['language']->translate('PLUGIN_FORM.FILEUPLOAD_UNABLE_TO_UPLOAD', null, true), $upload->file->name, $this->upload_errors[$upload->file->error])]; } else { // Remove the error object to avoid storing it unset($upload->file->error); // we need to move the file at this stage or else // it won't be available upon save later on // since php removes it from the upload location $tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true); $tmp_file = $upload->file->tmp_name; $tmp = $tmp_dir . '/uploaded-files/' . basename($tmp_file); Folder::create(dirname($tmp)); if (!move_uploaded_file($tmp_file, $tmp)) { // json_response return ['status' => 'error', 'message' => sprintf($grav['language']->translate('PLUGIN_FORM.FILEUPLOAD_UNABLE_TO_MOVE', null, true), '', $tmp)]; } $upload->file->tmp_name = $tmp; } // Handle file size limits $settings->filesize *= 1048576; // 2^20 [MB in Bytes] if ($settings->filesize > 0 && $upload->file->size > $settings->filesize) { // json_response return ['status' => 'error', 'message' => $grav['language']->translate('PLUGIN_FORM.EXCEEDED_GRAV_FILESIZE_LIMIT')]; } // Handle Accepted file types // Accept can only be mime types (image/png | image/*) or file extensions (.pdf|.jpg) $accepted = false; $errors = []; foreach ((array) $settings->accept as $type) { // Force acceptance of any file when star notation if ($type == '*') { $accepted = true; break; } $isMime = strstr($type, '/'); $find = str_replace('*', '.*', $type); $match = preg_match('#' . $find . '$#', $isMime ? $upload->file->type : $upload->file->name); if (!$match) { $message = $isMime ? 'The MIME type "' . $upload->file->type . '"' : 'The File Extension'; $errors[] = $message . ' for the file "' . $upload->file->name . '" is not an accepted.'; $accepted |= false; } else { $accepted |= true; } } if (!$accepted) { // json_response return ['status' => 'error', 'message' => implode('<br />', $errors)]; } // Retrieve the current session of the uploaded files for the field // and initialize it if it doesn't exist $sessionField = base64_encode($uri); $flash = $session->getFlashObject('files-upload'); if (!$flash) { $flash = []; } if (!isset($flash[$sessionField])) { $flash[$sessionField] = []; } if (!isset($flash[$sessionField][$upload->field])) { $flash[$sessionField][$upload->field] = []; } // Set destination $destination = Folder::getRelativePath(rtrim($settings->destination, '/')); $destination = $this->getPagePathFromToken($destination); // Create destination if needed if (!is_dir($destination)) { Folder::mkdir($destination); } // Generate random name if required if ($settings->random_name) { $extension = pathinfo($upload->file->name)['extension']; $upload->file->name = Utils::generateRandomString(15) . '.' . $extension; } // Handle conflicting name if needed if ($settings->avoid_overwriting) { if (file_exists($destination . '/' . $upload->file->name)) { $upload->file->name = date('YmdHis') . '-' . $upload->file->name; } } // Prepare object for later save $path = $destination . '/' . $upload->file->name; $upload->file->path = $path; // $upload->file->route = $page ? $path : null; // Prepare data to be saved later $flash[$sessionField][$upload->field][$path] = (array) $upload->file; // Finally store the new uploaded file in the field session $session->setFlashObject('files-upload', $flash); // json_response return ['status' => 'success', 'session' => \json_encode(['sessionField' => base64_encode($uri), 'path' => $upload->file->path, 'field' => $settings->name])]; }
/** * Handles ajax upload for files. * Stores in a flash object the temporary file and deals with potential file errors. * * @return bool True if the action was performed. */ public function taskFilesUpload() { if (!$this->authorizeTask('save', $this->dataPermissions()) || !isset($_FILES)) { return false; } /** @var Config $config */ $config = $this->grav['config']; $data = $this->view == 'pages' ? $this->admin->page(true) : $this->prepareData([]); $settings = $data->blueprints()->schema()->getProperty($this->post['name']); $settings = (object) array_merge(['avoid_overwriting' => false, 'random_name' => false, 'accept' => ['image/*'], 'limit' => 10, 'filesize' => $config->get('system.media.upload_limit', 5242880)], (array) $settings, ['name' => $this->post['name']]); $upload = $this->normalizeFiles($_FILES['data'], $settings->name); if (!isset($settings->destination)) { $this->admin->json_response = ['status' => 'error', 'message' => $this->admin->translate('PLUGIN_ADMIN.DESTINATION_NOT_SPECIFIED', null, true)]; return false; } // Do not use self@ outside of pages if ($this->view != 'pages' && in_array($settings->destination, ['@self', 'self@'])) { $this->admin->json_response = ['status' => 'error', 'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_PREVENT_SELF', null, true), $settings->destination)]; return false; } // Handle errors and breaks without proceeding further if ($upload->file->error != UPLOAD_ERR_OK) { $this->admin->json_response = ['status' => 'error', 'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD', null, true), $upload->file->name, $this->upload_errors[$upload->file->error])]; return false; } else { // Remove the error object to avoid storing it unset($upload->file->error); // we need to move the file at this stage or else // it won't be available upon save later on // since php removes it from the upload location $tmp_dir = Admin::getTempDir(); $tmp_file = $upload->file->tmp_name; $tmp = $tmp_dir . '/uploaded-files/' . basename($tmp_file); Folder::create(dirname($tmp)); if (!move_uploaded_file($tmp_file, $tmp)) { $this->admin->json_response = ['status' => 'error', 'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_MOVE', null, true), '', $tmp)]; return false; } $upload->file->tmp_name = $tmp; } // Handle file size limits $settings->filesize *= 1048576; // 2^20 [MB in Bytes] if ($settings->filesize > 0 && $upload->file->size > $settings->filesize) { $this->admin->json_response = ['status' => 'error', 'message' => $this->admin->translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT')]; return false; } // Handle Accepted file types // Accept can only be mime types (image/png | image/*) or file extensions (.pdf|.jpg) $accepted = false; $errors = []; foreach ((array) $settings->accept as $type) { // Force acceptance of any file when star notation if ($type == '*') { $accepted = true; break; } $isMime = strstr($type, '/'); $find = str_replace('*', '.*', $type); $match = preg_match('#' . $find . '$#', $isMime ? $upload->file->type : $upload->file->name); if (!$match) { $message = $isMime ? 'The MIME type "' . $upload->file->type . '"' : 'The File Extension'; $errors[] = $message . ' for the file "' . $upload->file->name . '" is not an accepted.'; $accepted |= false; } else { $accepted |= true; } } if (!$accepted) { $this->admin->json_response = ['status' => 'error', 'message' => implode('<br />', $errors)]; return false; } // Retrieve the current session of the uploaded files for the field // and initialize it if it doesn't exist $sessionField = base64_encode($this->uri); $flash = $this->admin->session()->getFlashObject('files-upload'); if (!$flash) { $flash = []; } if (!isset($flash[$sessionField])) { $flash[$sessionField] = []; } if (!isset($flash[$sessionField][$upload->field])) { $flash[$sessionField][$upload->field] = []; } // Set destination $destination = Folder::getRelativePath(rtrim($settings->destination, '/')); $destination = $this->admin->getPagePathFromToken($destination); // Create destination if needed if (!is_dir($destination)) { Folder::mkdir($destination); } // Generate random name if required if ($settings->random_name) { // TODO: document $extension = pathinfo($upload->file->name)['extension']; $upload->file->name = Utils::generateRandomString(15) . '.' . $extension; } // Handle conflicting name if needed if ($settings->avoid_overwriting) { // TODO: document if (file_exists($destination . '/' . $upload->file->name)) { $upload->file->name = date('YmdHis') . '-' . $upload->file->name; } } // Prepare object for later save $path = $destination . '/' . $upload->file->name; $upload->file->path = $path; // $upload->file->route = $page ? $path : null; // Prepare data to be saved later $flash[$sessionField][$upload->field][$path] = (array) $upload->file; // Finally store the new uploaded file in the field session $this->admin->session()->setFlashObject('files-upload', $flash); $this->admin->json_response = ['status' => 'success', 'session' => \json_encode(['sessionField' => base64_encode($this->uri), 'path' => $upload->file->path, 'field' => $settings->name])]; return true; }
/** * Used by the filepicker field to get a list of files in a folder. */ protected function taskGetFilesInFolder() { if (!$this->authorizeTask('save', $this->dataPermissions())) { return false; } $data = $this->view == 'pages' ? $this->admin->page(true) : $this->prepareData([]); $settings = $data->blueprints()->schema()->getProperty($this->post['name']); if (isset($settings['folder'])) { $folder = $settings['folder']; } else { $folder = '@self'; } // Do not use self@ outside of pages if ($this->view != 'pages' && in_array($folder, ['@self', 'self@'])) { $this->admin->json_response = ['status' => 'error', 'message' => sprintf($this->admin->translate('PLUGIN_ADMIN.FILEUPLOAD_PREVENT_SELF', null), $folder)]; return false; } // Set destination $folder = Folder::getRelativePath(rtrim($folder, '/')); $folder = $this->admin->getPagePathFromToken($folder); $media = new Media($folder); $available_files = []; foreach ($media->all() as $name => $medium) { $available_files[] = $name; } // Peak in the flashObject for optimistic filepicker updates $pending_files = []; $sessionField = base64_encode($this->grav['uri']->url()); $flash = $this->admin->session()->getFlashObject('files-upload'); if ($flash && isset($flash[$sessionField])) { foreach ($flash[$sessionField] as $field => $data) { foreach ($data as $file) { if (dirname($file['path']) === $folder) { $pending_files[] = $file['name']; } } } } $this->admin->session()->setFlashObject('files-upload', $flash); // Handle Accepted file types // Accept can only be file extensions (.pdf|.jpg) if (isset($settings['accept'])) { $available_files = array_filter($available_files, function ($file) use($settings) { return $this->filterAcceptedFiles($file, $settings); }); $pending_files = array_filter($pending_files, function ($file) use($settings) { return $this->filterAcceptedFiles($file, $settings); }); } $this->admin->json_response = ['status' => 'success', 'files' => array_values($available_files), 'pending' => array_values($pending_files), 'folder' => $folder]; return true; }