Skip to content

Commit 101ad1d

Browse files
authored
Fix subdirectory slim routing as a got fix for 5.22.0 (#7558)
## What Changed <!-- Short summary - what and why (not how) --> What Changed Update SlimUtils::getBasePath() to auto-detect subdirectory from Config.php Combine $sRootPath with endpoint path (/api, /v2, /session, etc.) Replace manual path parsing in session, external, and kiosk entry points Add proper import statements to all Slim entry points Update .htaccess documentation for subdirectory installations Add import statement rules to copilot-instructions.md Resolves routing 404 errors when ChurchCRM is installed in subdirectories like /churchcrm5220/ ## Type <!-- Check one --> - [ ] ✨ Feature - [x] 🐛 Bug fix - [ ] ♻️ Refactor - [ ] 🏗️ Build/Infrastructure - [ ] 🔒 Security ## Testing <!-- How to verify this works --> ## Screenshots <!-- Only for UI changes - drag & drop images here --> ## Security Check <!-- Only check if applicable --> - [ ] Introduces new input validation - [ ] Modifies authentication/authorization - [ ] Affects data privacy/GDPR ### Code Quality - [ ] Database: Propel ORM only, no raw SQL - [ ] No deprecated attributes (align, valign, nowrap, border, cellpadding, cellspacing, bgcolor) - [ ] Bootstrap CSS classes used - [ ] All CSS bundled via webpack ## Pre-Merge - [ ] Tested locally - [ ] No new warnings - [ ] Build passes - [ ] Backward compatible (or migration documented)
2 parents 00ac2b5 + dea73bb commit 101ad1d

File tree

11 files changed

+114
-59
lines changed

11 files changed

+114
-59
lines changed

src/.htaccess

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ Options -Indexes
33

44
RewriteEngine On
55

6-
# Some hosts may require you to use the `RewriteBase` directive.
7-
# If you need to use the `RewriteBase` directive, it should be the
8-
# absolute physical path to the directory that contains this htaccess file.
9-
#
10-
# RewriteBase /
6+
# Automatically handle subdirectory installations
7+
# The RewriteRule will pass the matched path to index.php
8+
# ChurchCRM uses the $sRootPath from Config.php to handle subdirectories
9+
#
10+
# NOTE: If you have issues with routing, you may need to set RewriteBase manually:
11+
# For root installation (/): RewriteBase /
12+
# For subdirectory (/churchcrm): RewriteBase /churchcrm
13+
# For subdirectory (/xyz): RewriteBase /xyz
1114

1215
RewriteCond %{REQUEST_FILENAME} !-f
1316
RewriteRule ^(.*)$ index.php [QSA,L]

src/ChurchCRM/Slim/SlimUtils.php

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
namespace ChurchCRM\Slim;
33

44
use ChurchCRM\dto\Photo;
5+
use ChurchCRM\dto\SystemURLs;
56
use ChurchCRM\Utils\LoggerUtils;
7+
use Exception;
8+
use Monolog\Handler\StreamHandler;
9+
use Monolog\Logger;
610
use Psr\Http\Message\ResponseInterface as Response;
711
use Psr\Http\Message\ServerRequestInterface as Request;
12+
use Slim\Exception\HttpMethodNotAllowedException;
813
use Slim\Exception\HttpNotFoundException;
14+
use Slim\HttpCache\CacheProvider;
915
use Slim\Interfaces\RouteInterface;
16+
use Slim\Psr7\Response as Psr7Response;
1017
use Slim\Routing\RouteContext;
11-
use Slim\HttpCache\CacheProvider;
12-
use Monolog\Logger;
13-
use Monolog\Handler\StreamHandler;
1418
use Throwable;
1519

1620

@@ -88,11 +92,33 @@ public static function setupErrorLogger($errorMiddleware)
8892
}
8993

9094
/**
91-
* Get Slim base path from environment or default
95+
* Get Slim base path from environment or calculate from SystemURLs root path
96+
* This ensures Slim routes work correctly whether installed at root (/) or in a subdirectory (/churchcrm)
97+
*
98+
* @param string $endpoint The Slim application endpoint (/api or /v2) - REQUIRED
99+
* @return string The complete base path including subdirectory if applicable
92100
*/
93-
public static function getBasePath($default = '/api')
101+
public static function getBasePath(string $endpoint)
94102
{
95-
return getenv('SLIM_BASE_PATH') ?: $default;
103+
// Allow environment override for testing/special deployments
104+
if ($envPath = getenv('SLIM_BASE_PATH')) {
105+
return $envPath;
106+
}
107+
108+
// Get the root path from SystemURLs (configured in Config.php as $sRootPath)
109+
// Examples: '' (root install), '/churchcrm', '/crm', etc.
110+
try {
111+
$rootPath = SystemURLs::getRootPath();
112+
113+
// Combine root path with endpoint
114+
// If root is empty string (installed at /), just return endpoint
115+
// If root is /churchcrm, return /churchcrm/api or /churchcrm/v2
116+
return $rootPath . $endpoint;
117+
} catch (Exception $e) {
118+
// If SystemURLs not initialized yet, fall back to endpoint only
119+
// This shouldn't happen in normal operation but provides safety
120+
return $endpoint;
121+
}
96122
}
97123

98124
/**
@@ -119,13 +145,13 @@ public static function registerDefaultJsonErrorHandler($errorMiddleware)
119145
bool $logErrorDetails
120146
) use ($logger) {
121147
$logger->error($exception->getMessage(), ['exception' => $exception]);
122-
$response = new \Slim\Psr7\Response();
148+
$response = new Psr7Response();
123149

124150
// Determine appropriate HTTP status code based on exception type
125151
$statusCode = 500;
126-
if ($exception instanceof \Slim\Exception\HttpNotFoundException) {
152+
if ($exception instanceof HttpNotFoundException) {
127153
$statusCode = 404;
128-
} elseif ($exception instanceof \Slim\Exception\HttpMethodNotAllowedException) {
154+
} elseif ($exception instanceof HttpMethodNotAllowedException) {
129155
$statusCode = 405;
130156
}
131157

src/api/.htaccess

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
RewriteEngine On
22

3-
# Some hosts may require you to use the `RewriteBase` directive.
4-
# If you need to use the `RewriteBase` directive, it should be the
5-
# absolute physical path to the directory that contains this htaccess file.
6-
#
7-
# RewriteBase /
3+
# Automatically handle subdirectory installations
4+
# The RewriteRule will pass the matched path to index.php
5+
# Slim will use the base path from Config.php ($sRootPath) to match routes correctly
6+
#
7+
# NOTE: If you have issues with routing, you may need to set RewriteBase manually:
8+
# For root installation (/): RewriteBase /api
9+
# For subdirectory (/churchcrm): RewriteBase /churchcrm/api
10+
# For subdirectory (/xyz): RewriteBase /xyz/api
811

912
RewriteCond %{REQUEST_FILENAME} !-f
1013
RewriteRule ^(.*)$ index.php [QSA,L]

src/api/index.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
<?php
2+
3+
require_once '../Include/Config.php';
4+
require_once __DIR__ . '/../vendor/autoload.php';
5+
26
use ChurchCRM\Service\DepositService;
3-
use ChurchCRM\Slim\SlimUtils;
4-
use ChurchCRM\Slim\Middleware\AuthMiddleware;
5-
use ChurchCRM\Slim\Middleware\VersionMiddleware;
6-
use ChurchCRM\Slim\Middleware\CorsMiddleware;
77
use ChurchCRM\Service\FinancialService;
88
use ChurchCRM\Service\GroupService;
99
use ChurchCRM\Service\PersonService;
1010
use ChurchCRM\Service\SystemService;
11+
use ChurchCRM\Slim\Middleware\AuthMiddleware;
12+
use ChurchCRM\Slim\Middleware\CorsMiddleware;
13+
use ChurchCRM\Slim\Middleware\VersionMiddleware;
14+
use ChurchCRM\Slim\SlimUtils;
1115
use Slim\Factory\AppFactory;
1216
use Symfony\Component\DependencyInjection\ContainerBuilder;
1317

14-
require_once '../Include/Config.php';
15-
require_once __DIR__ . '/../vendor/autoload.php';
16-
17-
// Use SlimUtils to get base path, default to /api
18+
// Get base path by combining $sRootPath from Config.php with /api endpoint
19+
// Examples: '' + '/api' = '/api' (root install)
20+
// '/churchcrm' + '/api' = '/churchcrm/api' (subdirectory install)
1821
$basePath = SlimUtils::getBasePath('/api');
1922

2023

src/external/index.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
<?php
22

3-
use ChurchCRM\Slim\SlimUtils;
4-
use ChurchCRM\Slim\Middleware\VersionMiddleware;
3+
require_once '../Include/Config.php';
4+
require_once __DIR__ . '/../vendor/autoload.php';
5+
56
use ChurchCRM\Slim\Middleware\CorsMiddleware;
7+
use ChurchCRM\Slim\Middleware\VersionMiddleware;
8+
use ChurchCRM\Slim\SlimUtils;
69
use Slim\Factory\AppFactory;
710
use Symfony\Component\DependencyInjection\ContainerBuilder;
811

9-
require_once '../Include/Config.php';
10-
require_once __DIR__ . '/../vendor/autoload.php';
11-
12-
$rootPath = str_replace('/external/index.php', '', $_SERVER['SCRIPT_NAME']);
12+
// Get base path by combining $sRootPath from Config.php with /external endpoint
13+
// Examples: '' + '/external' = '/external' (root install)
14+
// '/churchcrm' + '/external' = '/churchcrm/external' (subdirectory install)
15+
$basePath = SlimUtils::getBasePath('/external');
1316

1417

1518
$container = new ContainerBuilder();
1619
$container->compile();
1720
AppFactory::setContainer($container);
1821
$app = AppFactory::create();
19-
$app->setBasePath($rootPath . '/external');
22+
$app->setBasePath($basePath);
2023

2124
// Add Slim error middleware for proper error handling and logging
2225
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

src/kiosk/index.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,31 @@
55
// This file is generated by Composer
66
require_once __DIR__ . '/../vendor/autoload.php';
77

8-
$rootPath = dirname($_SERVER['SCRIPT_NAME']);
98
use ChurchCRM\dto\SystemConfig;
109
use ChurchCRM\model\ChurchCRM\KioskDevice;
1110
use ChurchCRM\model\ChurchCRM\KioskDeviceQuery;
12-
use Slim\Factory\AppFactory;
13-
use Symfony\Component\DependencyInjection\ContainerBuilder;
1411
use ChurchCRM\Slim\Middleware\AuthMiddleware;
15-
use ChurchCRM\Slim\Middleware\VersionMiddleware;
1612
use ChurchCRM\Slim\Middleware\CorsMiddleware;
13+
use ChurchCRM\Slim\Middleware\VersionMiddleware;
1714
use ChurchCRM\Slim\SlimUtils;
15+
use Slim\Factory\AppFactory;
16+
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
18+
// Get base path by combining $sRootPath from Config.php with /kiosk endpoint
19+
// Examples: '' + '/kiosk' = '/kiosk' (root install)
20+
// '/churchcrm' + '/kiosk' = '/churchcrm/kiosk' (subdirectory install)
21+
$basePath = SlimUtils::getBasePath('/kiosk');
1822

1923
$container = new ContainerBuilder();
2024
// Register custom error handlers
2125
AppFactory::setContainer($container);
2226
$app = AppFactory::create();
23-
$app->setBasePath($rootPath . '/kiosk');
27+
$app->setBasePath($basePath);
2428

2529
// Add Slim error middleware for proper error handling and logging
2630
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
27-
\ChurchCRM\Slim\SlimUtils::setupErrorLogger($errorMiddleware);
28-
\ChurchCRM\Slim\SlimUtils::registerDefaultJsonErrorHandler($errorMiddleware);
31+
SlimUtils::setupErrorLogger($errorMiddleware);
32+
SlimUtils::registerDefaultJsonErrorHandler($errorMiddleware);
2933

3034
$app->addBodyParsingMiddleware();
3135
$app->addRoutingMiddleware();

src/session/index.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@
1717

1818
require_once __DIR__ . '/../vendor/autoload.php';
1919

20-
$rootPath = str_replace('/session/index.php', '', $_SERVER['SCRIPT_NAME']);
20+
// Get base path by combining $sRootPath from Config.php with /session endpoint
21+
// Examples: '' + '/session' = '/session' (root install)
22+
// '/churchcrm' + '/session' = '/churchcrm/session' (subdirectory install)
23+
$basePath = SlimUtils::getBasePath('/session');
2124

2225
$container = new ContainerBuilder();
2326
$container->compile();
2427
// Register custom error handlers
2528
AppFactory::setContainer($container);
2629
$app = AppFactory::create();
27-
$app->setBasePath($rootPath . '/session');
30+
$app->setBasePath($basePath);
2831

2932
// Add Slim error middleware for proper error handling and logging
3033
$errorMiddleware = $app->addErrorMiddleware(true, true, true);

src/setup/index.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515

1616
require_once __DIR__ . '/../vendor/autoload.php';
1717

18-
// Use SlimUtils to get base path, default to /setup
18+
// Use SlimUtils to get base path for routing, but assets are in parent directory
1919
$basePath = ChurchCRM\Slim\SlimUtils::getBasePath('/setup');
20-
SystemURLs::init($basePath, '', __DIR__ . '/../');
20+
// Initialize SystemURLs with parent directory root (where assets actually are)
21+
$parentRootPath = str_replace('/setup', '', $basePath);
22+
SystemURLs::init($parentRootPath, '', __DIR__ . '/../');
2123
SystemConfig::init();
2224

2325

src/setup/routes/setup.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,6 @@ function is_valid_port($value)
142142

143143
function is_valid_root_path($value)
144144
{
145-
return preg_match('#^\/[a-zA-Z0-9_\-\.\/]*$#', $value);
145+
// Allow empty string OR a path starting with / (no trailing slash)
146+
return preg_match('#^(|\/[a-zA-Z0-9_\-\.\/]*)$#', $value);
146147
}

src/v2/.htaccess

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
RewriteEngine On
22

3-
# Some hosts may require you to use the `RewriteBase` directive.
4-
# If you need to use the `RewriteBase` directive, it should be the
5-
# absolute physical path to the directory that contains this htaccess file.
6-
#
7-
# RewriteBase /
3+
# Automatically handle subdirectory installations
4+
# The RewriteRule will pass the matched path to index.php
5+
# Slim will use the base path from Config.php ($sRootPath) to match routes correctly
6+
#
7+
# NOTE: If you have issues with routing, you may need to set RewriteBase manually:
8+
# For root installation (/): RewriteBase /v2
9+
# For subdirectory (/churchcrm): RewriteBase /churchcrm/v2
10+
# For subdirectory (/xyz): RewriteBase /xyz/v2
11+
812
RewriteCond %{REQUEST_FILENAME} !-f
913
RewriteRule ^(.*)$ index.php [QSA,L]

0 commit comments

Comments
 (0)