diff --git a/lib/adminlib.php b/lib/adminlib.php index 3df2cc3d6ad..7540be3f73c 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -2543,9 +2543,10 @@ class admin_setting_configexecutable extends admin_setting_configfile { public function output_html($data, $query='') { global $CFG; $default = $this->get_defaultsetting(); + require_once("$CFG->libdir/filelib.php"); if ($data) { - if (file_exists($data) and !is_dir($data) and is_executable($data)) { + if (file_exists($data) and !is_dir($data) and file_is_executable($data)) { $executable = '✔'; } else { $executable = '✘'; diff --git a/lib/filelib.php b/lib/filelib.php index 246aaad3cb1..c26865d54c8 100644 --- a/lib/filelib.php +++ b/lib/filelib.php @@ -2659,6 +2659,35 @@ function file_modify_html_header($text) { return $text; } +/** + * Tells whether the filename is executable. + * + * @link http://php.net/manual/en/function.is-executable.php + * @link https://bugs.php.net/bug.php?id=41062 + * @param string $filename Path to the file. + * @return bool True if the filename exists and is executable; otherwise, false. + */ +function file_is_executable($filename) { + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + if (is_executable($filename)) { + return true; + } else { + $fileext = strrchr($filename, '.'); + // If we have an extension we can check if it is listed as executable. + if ($fileext && file_exists($filename) && !is_dir($filename)) { + $winpathext = strtolower(getenv('PATHEXT')); + $winpathexts = explode(';', $winpathext); + + return in_array(strtolower($fileext), $winpathexts); + } + + return false; + } + } else { + return is_executable($filename); + } +} + /** * RESTful cURL class * diff --git a/lib/filestorage/file_storage.php b/lib/filestorage/file_storage.php index 6ccb84e93d7..e4b1dde181f 100644 --- a/lib/filestorage/file_storage.php +++ b/lib/filestorage/file_storage.php @@ -224,7 +224,7 @@ class file_storage { protected function create_converted_document(stored_file $file, $format) { global $CFG; - if (empty($CFG->pathtounoconv) || !is_executable(trim($CFG->pathtounoconv))) { + if (empty($CFG->pathtounoconv) || !file_is_executable(trim($CFG->pathtounoconv))) { // No conversions are possible, sorry. return false; } diff --git a/lib/tests/unoconv_test.php b/lib/tests/unoconv_test.php index 3038abb3a5b..19817df320a 100644 --- a/lib/tests/unoconv_test.php +++ b/lib/tests/unoconv_test.php @@ -71,7 +71,7 @@ class core_unoconv_testcase extends advanced_testcase { public function test_generate_pdf() { global $CFG; - if (empty($CFG->pathtounoconv) || !is_executable(trim($CFG->pathtounoconv))) { + if (empty($CFG->pathtounoconv) || !file_is_executable(trim($CFG->pathtounoconv))) { // No conversions are possible, sorry. return $this->markTestSkipped(); } @@ -90,7 +90,7 @@ class core_unoconv_testcase extends advanced_testcase { public function test_generate_markdown() { global $CFG; - if (empty($CFG->pathtounoconv) || !is_executable(trim($CFG->pathtounoconv))) { + if (empty($CFG->pathtounoconv) || !file_is_executable(trim($CFG->pathtounoconv))) { // No conversions are possible, sorry. return $this->markTestSkipped(); } diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 172d9c2eab7..78cd9490d17 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -115,7 +115,8 @@ information provided here is intended especially for developers. actual URLs in $text to encoded URLs in the @@PLUGINFILE@@ form. * behat_util::is_server_running() is removed, please use behat_util::check_server_status() instead. * Behat\Mink\Selector\SelectorsHandler::xpathLiteral() method is deprecated use behat_context_helper::escape instead - when building Xpath, or pass the unescaped value when using the named selector.', + when building Xpath, or pass the unescaped value when using the named selector. +* Add new file_is_executable(), to consistently check for executables even in Windows (PHP bug #41062). === 3.0 ===