-
-
diff --git a/conf/themes/silverstripe/main_index.twig b/conf/themes/silverstripe/main_index.twig
deleted file mode 100644
index a498649..0000000
--- a/conf/themes/silverstripe/main_index.twig
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- {# NOTE: Do not put this file into the manifest.yml, as that would render it per version rather than as the main index file #}
-
-
-
diff --git a/conf/themes/silverstripe/main_search.twig b/conf/themes/silverstripe/main_search.twig
deleted file mode 100644
index d819029..0000000
--- a/conf/themes/silverstripe/main_search.twig
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
- {# NOTE: Do not put this file into the manifest.yml, as that would render it per version rather than in the base dir #}
-
- {%- endif %}
-
-
-
- {# Fall back to just sending users to the default search page if the redirect fails for some reason #}
-
-
-
diff --git a/errors/404.html b/errors/404.html
new file mode 100644
index 0000000..92bab30
--- /dev/null
+++ b/errors/404.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
Whoa 404, either we broke something or you had a typing mishap :-/
+
Let's go back home.
+
+
+
+
diff --git a/static/css/error-styles.css b/errors/error-styles.css
similarity index 88%
rename from static/css/error-styles.css
rename to errors/error-styles.css
index a46eb4b..ca56053 100644
--- a/static/css/error-styles.css
+++ b/errors/error-styles.css
@@ -1,5 +1,5 @@
.gif_holder {
- background-image: url('/images/page-not-found.gif');
+ background-image: url('https://www.silverstripe.com/themes/ssv3/img/banners/page-not-found.gif');
position: relative;
z-index: 807;
-webkit-background-size: cover;
@@ -52,5 +52,5 @@ a:hover {
body {
padding: 0;
- margin: 0;
-}
+ margin: 0;"
+}
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..d73b243
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,41 @@
+{
+ "name": "SilverStripe",
+ "icons": [
+ {
+ "src": "\/android-chrome-36x36.png",
+ "sizes": "36x36",
+ "type": "image\/png",
+ "density": "0.75"
+ },
+ {
+ "src": "\/android-chrome-48x48.png",
+ "sizes": "48x48",
+ "type": "image\/png",
+ "density": "1.0"
+ },
+ {
+ "src": "\/android-chrome-72x72.png",
+ "sizes": "72x72",
+ "type": "image\/png",
+ "density": "1.5"
+ },
+ {
+ "src": "\/android-chrome-96x96.png",
+ "sizes": "96x96",
+ "type": "image\/png",
+ "density": "2.0"
+ },
+ {
+ "src": "\/android-chrome-144x144.png",
+ "sizes": "144x144",
+ "type": "image\/png",
+ "density": "3.0"
+ },
+ {
+ "src": "\/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image\/png",
+ "density": "4.0"
+ }
+ ]
+}
diff --git a/search/lookup.php b/search/lookup.php
new file mode 100644
index 0000000..209fac3
--- /dev/null
+++ b/search/lookup.php
@@ -0,0 +1,14 @@
+setVersionMap(array(
+ '/^(\d+)[.].*$/' => '$1',
+));
+
+$lookup->handle();
diff --git a/src/Lookup.php b/src/Lookup.php
new file mode 100644
index 0000000..719d63b
--- /dev/null
+++ b/src/Lookup.php
@@ -0,0 +1,190 @@
+args = $args;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getArgs()
+ {
+ return $this->args;
+ }
+
+ /**
+ * Return an argument from the array
+ *
+ * @param string $key
+ * @return string
+ */
+ public function getArg($key)
+ {
+ if (array_key_exists($key, $this->args)) {
+ return $this->args[$key];
+ }
+ return null;
+ }
+
+ /**
+ * Redirect the user to where they need to go.
+ * E.g. http://api.silverstripe.org/search/lookup/?q=SilverStripe\ORM\HasManyList&version=4&module=framework
+ * redirects to /4/SilverStripe/ORM/HasManyList.html
+ *
+ * @param bool $return When true, the redirect URL will be returned instead of a header issued
+ * @return string|null
+ */
+ public function handle($return = false)
+ {
+ $url = $this->getURL();
+ if ($return) {
+ return $url;
+ }
+ header('Location: ' . $url);
+ return null;
+ }
+
+ /**
+ * Allow setting the "version mapping" that can be used to convert "4" to "master", etc
+ *
+ * Useful in the event that modules have strange branching strategies
+ *
+ * @param array $map
+ * @return $this
+ */
+ public function setVersionMap($map)
+ {
+ $this->versionMap = $map;
+ return $this;
+ }
+
+ /**
+ * Get the version from the URL, check for manually set version mapping
+ *
+ * @return string
+ */
+ public function getVersion()
+ {
+ $version = $this->getArg('version') ?: self::DEFAULT_BRANCH;
+ foreach ($this->versionMap as $rule => $substitution) {
+ // Check regular expression rule
+ if (strpos($rule, '/') === 0 && preg_match($rule, $version)) {
+ return preg_replace($rule, $substitution, $version);
+ }
+ // Check exact rule
+ if (strpos($rule, '/') === false && $rule === $version) {
+ return $substitution;
+ }
+ }
+ return $version;
+ }
+
+ /**
+ * Base dir
+ *
+ * @return string
+ */
+ public function getBaseDir()
+ {
+ return __DIR__ . '/..';
+ }
+
+ /**
+ * Set the server name to use for API reference links
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function setServerName($name)
+ {
+ $this->serverName = $name;
+ return $this;
+ }
+
+ /**
+ * Given a config determine the URL to navigate to
+ *
+ * @param array $searchConfig
+ * @return string
+ */
+ protected function getURLForClass(array $searchConfig): string
+ {
+ $searchPath = '/' . $this->getVersion() . '/' . str_replace('\\', '/', $searchConfig['class']) . '.html';
+
+ // If file doesn't exist, redirect to search
+ if (!file_exists($this->getBaseDir() . '/htdocs' . $searchPath)) {
+ return '/' . $this->getVersion() . '/search.html?search=' . urlencode($searchConfig['class']);
+ }
+
+ // Add hash-link on end
+ if ($searchConfig['property'] && $searchConfig['type']) {
+ $searchPath .= '#' . $searchConfig['type'] . '_' . $searchConfig['property'];
+ }
+
+ return $searchPath;
+ }
+
+ /**
+ * Get url for this search
+ *
+ * @return string
+ */
+ protected function getURL(): string
+ {
+ // Search
+ $searchOrig = $this->getArg('q');
+ if (!$searchOrig) {
+ return '/'; // Just go to home
+ }
+
+ $search = str_replace(array('()', '$'), '', $searchOrig);
+ $searchParts = preg_split('/(::|\->)/', $search);
+ $searchConfig = array();
+ if (count($searchParts) == 2) {
+ $searchConfig['class'] = $searchParts[0];
+ $searchConfig['property'] = $searchParts[1];
+ $searchConfig['type'] = (strpos($searchOrig, '()') !== false) ? 'method' : 'property';
+ } else {
+ $searchConfig['class'] = $search;
+ $searchConfig['property'] = '';
+ $searchConfig['type'] = 'class';
+ }
+
+ return $this->getURLForClass($searchConfig);
+ }
+}
diff --git a/src/Renderer/SilverStripeRenderer.php b/src/Renderer/SilverStripeRenderer.php
index 4d9ab1c..10b9929 100644
--- a/src/Renderer/SilverStripeRenderer.php
+++ b/src/Renderer/SilverStripeRenderer.php
@@ -8,33 +8,6 @@
class SilverStripeRenderer extends Renderer
{
- /**
- * @inheritDoc
- * This is implemented here to promote visibility to public
- */
- public function save(Project $project, $uri, $template, $variables)
- {
- return parent::save($project, $uri, $template, $variables);
- }
-
- /**
- * Initialise the renderer so we can render versionless templates.
- * This happens when rendering version templates normally, but we want to render the global stuff first.
- */
- public function init(Project $project): void
- {
- // This is all copied from the render() method
- $this->theme = $this->getTheme($project);
- $dirs = $this->theme->getTemplateDirs();
- // add parent directory to be able to extends the same template as the current one but in the parent theme
- foreach ($dirs as $dir) {
- $dirs[] = dirname($dir);
- }
- $this->twig->getLoader()->setPaths(array_unique($dirs));
- $this->twig->addGlobal('has_namespaces', $project->hasNamespaces());
- $this->twig->addGlobal('project', $project);
- }
-
protected function renderClassTemplates(array $classes, Project $project, $callback = null)
{
foreach ($classes as $class) {
diff --git a/src/SilverstripeProject.php b/src/SilverstripeProject.php
deleted file mode 100644
index cd5bb85..0000000
--- a/src/SilverstripeProject.php
+++ /dev/null
@@ -1,75 +0,0 @@
-renderMainPages();
- $this->copyStaticFiles();
- parent::update($callback, $force);
- $this->outputStatus();
- }
-
- public function render($callback = null, $force = false): void
- {
- $this->renderMainPages();
- $this->copyStaticFiles();
- parent::render($callback, $force);
- $this->outputStatus();
- }
-
- /**
- * Renders pages that are not version dependent such as the main index.html
- */
- private function renderMainPages(): void
- {
- $this->version = '';
- $this->read();
- $config = Config::getConfig();
- $variables = [
- // Get array of strings of major version numbers
- 'versions' => array_map(fn($version) => (string)$version, array_keys($config['versions'])),
- 'default_version' => $config['default_version'],
- ];
- $this->renderer->init($this);
- $this->renderer->save($this, 'index.html', 'main_index.twig', $variables);
- $this->renderer->save($this, 'search.html', 'main_search.twig', $variables);
- $this->renderer->save($this, '404.html', '404.twig', $variables);
- }
-
- /**
- * Copies static files such as images or css into the build directory
- */
- private function copyStaticFiles(): void
- {
- $this->filesystem->mirror(Path::join(Config::getBase(), 'static'), $this->getBuildDir(), options: ['override' => true]);
- }
-
- /**
- * Outputs the number of items in htdocs. This can be a useful debugging tool if the site doesn't build correctly.
- */
- private function outputStatus(): void
- {
- $fileIterator = new FilesystemIterator($this->getBuildDir(), FilesystemIterator::SKIP_DOTS);
- $numItems = iterator_count($fileIterator);
- $output = new ConsoleOutput();
- $output->writeln("Found $numItems files/directories in htdocs");
- }
-
- protected function replaceVars(string $pattern): string
- {
- if (!$this->version) {
- // Remove the version pattern if we're rendering stuff directly into htdocs
- $pattern = str_replace('%version%/', '', $pattern);
- }
- return parent::replaceVars($pattern);
- }
-}
diff --git a/static/favicon.ico b/static/favicon.ico
deleted file mode 100644
index f5d53b6..0000000
Binary files a/static/favicon.ico and /dev/null differ
diff --git a/static/images/page-not-found.gif b/static/images/page-not-found.gif
deleted file mode 100644
index ebd084b..0000000
Binary files a/static/images/page-not-found.gif and /dev/null differ
diff --git a/tests/LookupTest.php b/tests/LookupTest.php
new file mode 100644
index 0000000..c12cb5c
--- /dev/null
+++ b/tests/LookupTest.php
@@ -0,0 +1,54 @@
+ 'bar']);
+ $this->assertSame(['foo' => 'bar'], $lookup->getArgs());
+ $this->assertSame('bar', $lookup->getArg('foo'));
+ }
+
+ public function testGetUrl()
+ {
+ $mock = $this->createPartialMock(Lookup::class, ['getURL']);
+ $mock->expects($this->once())->method('getURL')->willReturn('foobar');
+
+ $this->assertSame('foobar', $mock->handle(true));
+ }
+
+ public function testGetVersionChecksRegularExpression()
+ {
+ $lookup = new Lookup(['version' => 'foobarbaz']);
+ $lookup->setVersionMap([
+ '/bar/' => 'monkey',
+ ]);
+ $this->assertSame('foomonkeybaz', $lookup->getVersion());
+ }
+
+ public function testGetVersionExactRule()
+ {
+ $lookup = new Lookup(['version' => '5']);
+ $lookup->setVersionMap([
+ '5' => '5',
+ ]);
+ $this->assertSame('5', $lookup->getVersion());
+ }
+
+ public function testGetVersionDefault()
+ {
+ $lookup = new Lookup(['version' => 'unknown']);
+ $lookup->setVersionMap([
+ '5' => '5.x',
+ '4' => '4.x',
+ '3' => '3.x',
+ ]);
+
+ $this->assertSame('unknown', $lookup->getVersion());
+ }
+}