<commit_before><?php

/**
 * LICENSE: This source code is subject to the license that is available
 * in the LICENSE file distributed along with this package.
 *
 * @package    Penelope
 * @author     <NAME> <<EMAIL>>
 * @copyright  Karwana Ltd
 * @since      File available since Release 1.0.0
 */

namespace Karwana\Penelope;

const VERSION = '1.0.0';

use Closure;

use Slim;
use Everyman\Neo4j;

Slim\Route::setDefaultConditions(array(
	'node_id' => '\d+',
	'edge_id' => '\d+'
));

class Penelope extends OptionContainer {

	private $schema, $app, $client;

	public function __construct(Neo4j\Client $client, Slim\Slim $app, DefaultTheme $theme = null, array $options = null) {
		parent::__construct($options);

		$this->app = $app;
		$this->client = $client;
		$this->schema = new Schema($client);

		if ($theme) {
			$this->setTheme($theme);
		}

		if ($this->hasOption('upload.directory')) {
			$this->setUploadDirectory($this->getOption('upload.directory'));
		}

		// Set up the home route.
		$app->get('/', Closure::bind(function() {
			$controller = new Controllers\HomeController($this->app, $this->schema, $this->client);
			$controller->read();
		}, $this));

		// TODO: Make the uploads slug configurable using the same suggested method as for search (below).
		// Set up the uploads route.
		$app->get('/uploads/:file_name', Closure::bind(function($file_name) {
			$controller = new Controllers\UploadController($this->app);
			$controller->read($file_name);
		}, $this));

		// TODO: Use the same technique using getPathFormat as used by ObjectSchema. Perhaps hive off those methods into a separate PathFormatter class.
		if ($this->hasOption('search.slug')) {
			$search_slug = $this->getOption('search.slug');
		} else {
			$search_slug = 'search';
		}

		// Set up the search controller.
		$app->get('/' . $search_slug, Closure::bind(function() {
			$controller = new Controllers\SearchController($this->app, $this->schema, $this->client);
			$controller->run();
		}, $this))->name('search');

		// Set up a default handler for 404 errors.
		// Only Penelope application-generated exceptions are permitted.
		$app->notFound(function(Exceptions\Exception $e = null) {
			if (!$e) {
				$e = new Exceptions\NotFoundException('The requested page cannot be found.');
			}

			$controller = new Controllers\Controller($this->app);
			$this->app->render('error', array('title' => $controller->_m('error_404_title'), 'error' => $e), 404);
		});
	}

	public function getClient() {
		return $this->client;
	}

	public function getSchema() {
		return $this->schema;
	}

	public function getApp() {
		return $this->app;
	}

	public function setTheme(DefaultTheme $theme) {
		$old_theme = $this->getTheme();
		$this->app->view($theme);

		$pattern = '/' . $theme::ROUTE_SLUG . '/:resource_path+';

		// Slim doesn't allow a named route to be removed or overwritten once added, so some trickery is needed to rename it.
		if ($old_theme) {
			$route = $this->app->router->getNamedRoute($old_theme::ROUTE_NAME);
			$route->setName($theme::ROUTE_NAME);
			$route->setPattern($pattern);
			return;
		}

		$this->app->get($pattern, Closure::bind(function(array $resource_path) {
			$theme = $this->getTheme();

			// Pass if not an instance or child of the default theme, as DefaultTheme#renderResource won't be present.
			// In non-standard use cases, this allows the user to use a regular Slim\View as the view.
			if (!$theme) {
				$this->getApp()->pass();
			}

			$controller = new Controllers\FileController($this->app);
			if ($theme->hasResource($resource_path)) {
				$controller->read($theme->getResourcePath($resource_path));
			} else {
				$$this->getApp()->notFound(new Exceptions\Exception('Unknown resource "' . implode('/', $resource_path) . '".'));
			}

		}, $this))->name($theme::ROUTE_NAME);
	}

	public function getTheme() {
		$view = $this->app->view();

		if ($view instanceof DefaultTheme) {
			return $view;
		}
	}

	public function setUploadDirectory($directory) {
		Types\File::setSystemDirectory($directory);
	}

	// TODO: Figure out a way of avoiding use of the title key.
	public function addPage($name, $slug, $template, $title_key, \Closure $view_data_generator) {
		$this->app->get('/' . $slug, Closure::bind(function() use ($template, $view_data_generator) {
<commit_msg>Fix double dollar<commit_after><?php

/**
 * LICENSE: This source code is subject to the license that is available
 * in the LICENSE file distributed along with this package.
 *
 * @package    Penelope
 * @author     <NAME> <<EMAIL>>
 * @copyright  Karwana Ltd
 * @since      File available since Release 1.0.0
 */

namespace Karwana\Penelope;

const VERSION = '1.0.0';

use Closure;

use Slim;
use Everyman\Neo4j;

Slim\Route::setDefaultConditions(array(
	'node_id' => '\d+',
	'edge_id' => '\d+'
));

class Penelope extends OptionContainer {

	private $schema, $app, $client;

	public function __construct(Neo4j\Client $client, Slim\Slim $app, DefaultTheme $theme = null, array $options = null) {
		parent::__construct($options);

		$this->app = $app;
		$this->client = $client;
		$this->schema = new Schema($client);

		if ($theme) {
			$this->setTheme($theme);
		}

		if ($this->hasOption('upload.directory')) {
			$this->setUploadDirectory($this->getOption('upload.directory'));
		}

		// Set up the home route.
		$app->get('/', Closure::bind(function() {
			$controller = new Controllers\HomeController($this->app, $this->schema, $this->client);
			$controller->read();
		}, $this));

		// TODO: Make the uploads slug configurable using the same suggested method as for search (below).
		// Set up the uploads route.
		$app->get('/uploads/:file_name', Closure::bind(function($file_name) {
			$controller = new Controllers\UploadController($this->app);
			$controller->read($file_name);
		}, $this));

		// TODO: Use the same technique using getPathFormat as used by ObjectSchema. Perhaps hive off those methods into a separate PathFormatter class.
		if ($this->hasOption('search.slug')) {
			$search_slug = $this->getOption('search.slug');
		} else {
			$search_slug = 'search';
		}

		// Set up the search controller.
		$app->get('/' . $search_slug, Closure::bind(function() {
			$controller = new Controllers\SearchController($this->app, $this->schema, $this->client);
			$controller->run();
		}, $this))->name('search');

		// Set up a default handler for 404 errors.
		// Only Penelope application-generated exceptions are permitted.
		$app->notFound(function(Exceptions\Exception $e = null) {
			if (!$e) {
				$e = new Exceptions\NotFoundException('The requested page cannot be found.');
			}

			$controller = new Controllers\Controller($this->app);
			$this->app->render('error', array('title' => $controller->_m('error_404_title'), 'error' => $e), 404);
		});
	}

	public function getClient() {
		return $this->client;
	}

	public function getSchema() {
		return $this->schema;
	}

	public function getApp() {
		return $this->app;
	}

	public function setTheme(DefaultTheme $theme) {
		$old_theme = $this->getTheme();
		$this->app->view($theme);

		$pattern = '/' . $theme::ROUTE_SLUG . '/:resource_path+';

		// Slim doesn't allow a named route to be removed or overwritten once added, so some trickery is needed to rename it.
		if ($old_theme) {
			$route = $this->app->router->getNamedRoute($old_theme::ROUTE_NAME);
			$route->setName($theme::ROUTE_NAME);
			$route->setPattern($pattern);
			return;
		}

		$this->app->get($pattern, Closure::bind(function(array $resource_path) {
			$theme = $this->getTheme();

			// Pass if not an instance or child of the default theme, as DefaultTheme#renderResource won't be present.
			// In non-standard use cases, this allows the user to use a regular Slim\View as the view.
			if (!$theme) {
				$this->getApp()->pass();
			}

			$controller = new Controllers\FileController($this->app);
			if ($theme->hasResource($resource_path)) {
				$controller->read($theme->getResourcePath($resource_path));
			} else {
				$this->getApp()->notFound(new Exceptions\Exception('Unknown resource "' . implode('/', $resource_path) . '".'));
			}

		}, $this))->name($theme::ROUTE_NAME);
	}

	public function getTheme() {
		$view = $this->app->view();

		if ($view instanceof DefaultTheme) {
			return $view;
		}
	}

	public function setUploadDirectory($directory) {
		Types\File::setSystemDirectory($directory);
	}

	// TODO: Figure out a way of avoiding use of the title key.
	public function addPage($name, $slug, $template, $title_key, \Closure $view_data_generator) {
		$this->app->get('/' . $slug, Closure::bind(function() use ($template, $view_data_generator) {

{'fraction_non_alphanumeric': 0.16595152499130233, 'fraction_numerical': 0.003942943291197959, 'mean_word_length': 4.087905604719764, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 127, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4472372', 'n_tokens_mistral': 3017, 'n_tokens_neox': 2740, 'n_words': 710}