diff --git a/wire/modules/System/SystemUpdater/SystemUpdate17.php b/wire/modules/System/SystemUpdater/SystemUpdate17.php
new file mode 100644
index 00000000..359b50b6
--- /dev/null
+++ b/wire/modules/System/SystemUpdater/SystemUpdate17.php
@@ -0,0 +1,206 @@
+wire()->addHookAfter('ProcessWire::ready', $this, 'executeAtReady');
+ return 0; // indicates we will update system version ourselves when ready
+ }
+
+ public function executeAtReady() {
+ $this->auto = true;
+ if(!$this->update()) return;
+ $this->message(
+ "Details: $this->detailsUrl",
+ Notice::allowMarkup
+ );
+ $this->updater->saveSystemVersion(17);
+ }
+
+ /**
+ * Apply the update
+ *
+ * @return bool
+ * @throws WireException
+ *
+ */
+ public function update() {
+
+ if(!$this->isApache()) {
+ $this->warning('Update skipped because Apache not detected');
+ $this->warning('Please see the details URL below on how to apply this update manually');
+ return true;
+ }
+
+ $blockAll =
+ "\n\n Require all denied\n" . // Apache 2.4+
+ "\n\n Order allow,deny\n Deny from all\n"; // Prior to Apache 2.4
+
+ $denyAll = str_replace("\n", "\n ", $blockAll); // indented blockAll
+
+ $rules = array(
+ 'all' => array(
+ 'label' => 'block all access',
+ 'content' => ltrim($blockAll),
+ ),
+ 'php' => array(
+ 'label' => 'block all PHP files',
+ 'content' => "$denyAll\n",
+ ),
+ 'rifc' => array(
+ 'label' => 'block some PHP files',
+ 'content' => "$denyAll\n",
+ )
+ );
+
+ $cachePath = $this->wire('config')->paths->cache;
+ $siteRootDir = rtrim($this->wire('config')->paths->site, '/');
+ $siteRootUrl = rtrim($this->wire('config')->urls->site, '/');
+ $sitePathRules = array(
+ '/' => 'rifc',
+ '/assets/' => 'php',
+ '/assets/cache/' => 'all',
+ '/assets/backups/' => 'all',
+ '/assets/logs/' => 'all',
+ '/templates/' => 'php',
+ '/modules/' => 'php',
+ );
+
+ /** @var WireFileTools $files */
+ $files = $this->wire('files');
+ $numErrors = 0;
+
+ foreach($sitePathRules as $dir => $ruleName) {
+
+ $rule = $rules[$ruleName];
+ $header = "# Start ProcessWire:pwb$ruleName (update 17)";
+ $footer = "# End ProcessWire:pwb$ruleName";
+ $summary = "# $rule[label] (optional fallback if root .htaccess missing)";
+ $location = "$siteRootUrl$dir.htaccess";
+ $content = "$header\n$summary\n$rule[content]\n$footer";
+ $file = $siteRootDir . $dir . '.htaccess';
+ $url = $siteRootUrl . $dir . '.htaccess';
+ $path = dirname($file);
+
+ if(!is_dir($path)) {
+ if($this->auto) $this->message("Skipped $url (directory not present)");
+ continue;
+ }
+
+ if(file_exists($file)) {
+ // existing .htaccess file already present
+ if($this->auto) $this->message("Skipped: $url (already exists)");
+ continue;
+ }
+
+ // no .htaccess file currently present
+ $writable = is_writable(dirname($file));
+ $data = $content;
+ $actionLabel = 'Created';
+
+ if(!$writable) {
+ // file not writable, so we will create a temporary file in cache instead (for optional manual copy)
+ $file = $cachePath . str_replace(array('/', "\\", '.', '--'), '-', trim(str_replace($siteRootUrl, 'site', $url), '/')) . '.txt';
+ $tmpUrl = str_replace($siteRootDir, $siteRootUrl, $file);
+ if(file_exists($file)) {
+ // file already exists so we can skip
+ if($this->auto) $this->message("Ignored: $tmpUrl (already exists)");
+ continue;
+ }
+ $writable = is_writable(dirname($file));
+ if($writable) {
+ $this->warning("Unable to write updates to '$url', so writing to '$tmpUrl' instead, in case you want to copy manually.");
+ $data = "# Intended location of this file: $location\n$data";
+ $actionLabel = 'Created';
+ }
+ $url = $tmpUrl;
+ }
+
+ if(!$writable) {
+ if($ruleName !== 'all') {
+ if($this->auto) $this->message("Ignored: $url");
+ } else {
+ $this->error("Unable to write: $url");
+ $numErrors++;
+ }
+ continue;
+ }
+
+ try {
+ if($files->filePutContents($file, "$data\n", LOCK_EX)) {
+ $this->message("$actionLabel: $url");
+ } else {
+ throw new WireException("Unable to write: $url");
+ }
+ } catch(\Exception $e) {
+ $this->error($e->getMessage());
+ $numErrors++;
+ }
+ }
+
+ return $numErrors === 0;
+ }
+
+ /**
+ * Are we running under Apache?
+ *
+ * @return bool
+ *
+ */
+ public function isApache() {
+
+ $software = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
+
+ if(stripos($software, 'microsoft-iis') !== false) return false;
+ if(stripos($software, 'nginx') !== false) return false;
+ if(stripos($software, 'apache') !== false) return true;
+
+ if(function_exists('apache_get_version') && stripos(apache_get_version(), 'Apache') !== false) return true;
+ if(function_exists('apache_get_modules') && in_array('mod_rewrite', apache_get_modules())) return true;
+ if(getenv('HTTP_MOD_REWRITE') == 'On') return true;
+
+ $rootPath = $this->wire('config')->paths->root;
+ if(file_exists($rootPath . 'Web.config') || file_exists($rootPath . 'web.config')) return false; // IIS
+ if(file_exists($rootPath . '.htaccess')) return true;
+
+ return false;
+ }
+
+ /**
+ * Is this update useful for this installation?
+ *
+ * @return bool
+ *
+ */
+ public function isUseful() {
+ if(!$this->isApache()) return false;
+ $f = '.htaccess';
+ $paths = $this->wire('config')->paths;
+ $assets = $paths->assets;
+ if(!file_exists($assets . $f)) return true;
+ if(!file_exists($assets . "logs/$f")) return true;
+ if(!file_exists($assets . "cache/$f")) return true;
+ if(!file_exists($assets . "backups/$f")) return true;
+ if(!file_exists($paths->templates . $f)) return true;
+ if(!file_exists($paths->site . $f)) return true;
+ if(!file_exists($paths->site . "modules/$f")) return true;
+ return false;
+ }
+
+}
+
diff --git a/wire/modules/System/SystemUpdater/SystemUpdater.module b/wire/modules/System/SystemUpdater/SystemUpdater.module
index 3178ac4f..c96d41a6 100644
--- a/wire/modules/System/SystemUpdater/SystemUpdater.module
+++ b/wire/modules/System/SystemUpdater/SystemUpdater.module
@@ -26,7 +26,7 @@ class SystemUpdater extends WireData implements Module, ConfigurableModule {
* This version number is important, as this updater keeps the systemVersion up with this version
*
*/
- 'version' => 16,
+ 'version' => 17,
);
}
diff --git a/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php b/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php
index 15befcef..d7a9f577 100644
--- a/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php
+++ b/wire/modules/System/SystemUpdater/SystemUpdaterChecks.php
@@ -79,7 +79,7 @@ class SystemUpdaterChecks extends Wire {
'checkWelcome',
'checkIndexFile',
'checkHtaccessFile',
- //'checkOtherHtaccessFiles',
+ 'checkOtherHtaccessFiles',
'checkInstallerFiles',
'checkFilePermissions',
'checkPublishedField',