diff --git a/app/Console/Command/AbstractCommand.php b/app/Console/Command/AbstractCommand.php
new file mode 100644
index 00000000..d87269a9
--- /dev/null
+++ b/app/Console/Command/AbstractCommand.php
@@ -0,0 +1,64 @@
+input = $input;
+ $this->output = $output;
+
+ $this->fire();
+ }
+
+ /**
+ * Fire the command.
+ */
+ abstract protected function fire();
+
+ /**
+ * Did the user pass the given option?
+ *
+ * @param string $name
+ * @return bool
+ */
+ protected function hasOption(string $name)
+ {
+ return $this->input->hasOption($name);
+ }
+
+ /**
+ * Send an info string to the user.
+ *
+ * @param string $string
+ */
+ protected function info(string $string)
+ {
+ $this->output->writeln("$string");
+ }
+}
diff --git a/app/Database/AbstractModel.php b/app/Database/AbstractModel.php
new file mode 100644
index 00000000..79a83183
--- /dev/null
+++ b/app/Database/AbstractModel.php
@@ -0,0 +1,204 @@
+releaseAfterSaveCallbacks() as $callback) {
+ $callback($model);
+ }
+ });
+
+ static::deleted(function (AbstractModel $model) {
+ foreach ($model->releaseAfterDeleteCallbacks() as $callback) {
+ $callback($model);
+ }
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(array $attributes = [])
+ {
+ $defaults = [];
+
+ static::$dispatcher->fire(
+ new ConfigureModelDefaultAttributes($this, $defaults)
+ );
+
+ $this->attributes = $defaults;
+
+ parent::__construct($attributes);
+ }
+
+ /**
+ * Get the attributes that should be converted to dates.
+ *
+ * @return array
+ */
+ public function getDates()
+ {
+ static $dates = [];
+
+ $class = get_class($this);
+
+ if (! isset($dates[$class])) {
+ static::$dispatcher->fire(
+ new ConfigureModelDates($this, $this->dates)
+ );
+
+ $dates[$class] = $this->dates;
+ }
+
+ return $dates[$class];
+ }
+
+ /**
+ * Get an attribute from the model. If nothing is found, attempt to load
+ * a custom relation method with this key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function getAttribute($key)
+ {
+ if (! is_null($value = parent::getAttribute($key))) {
+ return $value;
+ }
+
+ // If a custom relation with this key has been set up, then we will load
+ // and return results from the query and hydrate the relationship's
+ // value on the "relationships" array.
+ if (! $this->relationLoaded($key) && ($relation = $this->getCustomRelation($key))) {
+ if (! $relation instanceof Relation) {
+ throw new LogicException(
+ 'Relationship method must return an object of type '.Relation::class
+ );
+ }
+
+ return $this->relations[$key] = $relation->getResults();
+ }
+ }
+
+ /**
+ * Get a custom relation object.
+ *
+ * @param string $name
+ * @return mixed
+ */
+ protected function getCustomRelation($name)
+ {
+ return static::$dispatcher->until(
+ new GetModelRelationship($this, $name)
+ );
+ }
+
+ /**
+ * Register a callback to be run once after the model is saved.
+ *
+ * @param callable $callback
+ * @return void
+ */
+ public function afterSave($callback)
+ {
+ $this->afterSaveCallbacks[] = $callback;
+ }
+
+ /**
+ * Register a callback to be run once after the model is deleted.
+ *
+ * @param callable $callback
+ * @return void
+ */
+ public function afterDelete($callback)
+ {
+ $this->afterDeleteCallbacks[] = $callback;
+ }
+
+ /**
+ * @return callable[]
+ */
+ public function releaseAfterSaveCallbacks()
+ {
+ $callbacks = $this->afterSaveCallbacks;
+
+ $this->afterSaveCallbacks = [];
+
+ return $callbacks;
+ }
+
+ /**
+ * @return callable[]
+ */
+ public function releaseAfterDeleteCallbacks()
+ {
+ $callbacks = $this->afterDeleteCallbacks;
+
+ $this->afterDeleteCallbacks = [];
+
+ return $callbacks;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __call($method, $arguments)
+ {
+ if ($relation = $this->getCustomRelation($method)) {
+ return $relation;
+ }
+
+ return parent::__call($method, $arguments);
+ }
+}
diff --git a/app/Database/DatabaseMigrationRepository.php b/app/Database/DatabaseMigrationRepository.php
new file mode 100644
index 00000000..03f2a55e
--- /dev/null
+++ b/app/Database/DatabaseMigrationRepository.php
@@ -0,0 +1,163 @@
+table = $table;
+ $this->resolver = $resolver;
+ }
+
+ /**
+ * Get the ran migrations.
+ *
+ * @return array
+ */
+ public function getRan($extension = null)
+ {
+ return $this->table()
+ ->where('extension', $extension)
+ ->orderBy('migration', 'asc')
+ ->lists('migration');
+ }
+
+ /**
+ * Log that a migration was run.
+ *
+ * @param string $file
+ * @param string $extension
+ * @return void
+ */
+ public function log($file, $extension = null)
+ {
+ $record = ['migration' => $file, 'extension' => $extension];
+
+ $this->table()->insert($record);
+ }
+
+ /**
+ * Remove a migration from the log.
+ *
+ * @param string $file
+ * @param string $extension
+ * @return void
+ */
+ public function delete($file, $extension = null)
+ {
+ $query = $this->table()->where('migration', $file);
+
+ if (is_null($extension)) {
+ $query->whereNull('extension');
+ } else {
+ $query->where('extension', $extension);
+ }
+
+ $query->delete();
+ }
+
+ /**
+ * Create the migration repository data store.
+ *
+ * @return void
+ */
+ public function createRepository()
+ {
+ $schema = $this->getConnection()->getSchemaBuilder();
+
+ $schema->create($this->table, function ($table) {
+ $table->string('migration');
+ $table->string('extension')->nullable();
+ });
+ }
+
+ /**
+ * Determine if the migration repository exists.
+ *
+ * @return bool
+ */
+ public function repositoryExists()
+ {
+ $schema = $this->getConnection()->getSchemaBuilder();
+
+ return $schema->hasTable($this->table);
+ }
+
+ /**
+ * Get a query builder for the migration table.
+ *
+ * @return \Illuminate\Database\Query\Builder
+ */
+ protected function table()
+ {
+ return $this->getConnection()->table($this->table);
+ }
+
+ /**
+ * Get the connection resolver instance.
+ *
+ * @return \Illuminate\Database\ConnectionResolverInterface
+ */
+ public function getConnectionResolver()
+ {
+ return $this->resolver;
+ }
+
+ /**
+ * Resolve the database connection instance.
+ *
+ * @return \Illuminate\Database\Connection
+ */
+ public function getConnection()
+ {
+ return $this->resolver->connection($this->connection);
+ }
+
+ /**
+ * Set the information source to gather data.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setSource($name)
+ {
+ $this->connection = $name;
+ }
+}
diff --git a/app/Database/DatabaseServiceProvider.php b/app/Database/DatabaseServiceProvider.php
new file mode 100644
index 00000000..f6f3a13c
--- /dev/null
+++ b/app/Database/DatabaseServiceProvider.php
@@ -0,0 +1,67 @@
+app->singleton('mybb.db', function () {
+ $factory = new ConnectionFactory($this->app);
+
+ $connection = $factory->make($this->app->config('database'));
+ $connection->setEventDispatcher($this->app->make('Illuminate\Contracts\Events\Dispatcher'));
+ $connection->setFetchMode(PDO::FETCH_CLASS);
+
+ return $connection;
+ });
+
+ $this->app->alias('mybb.db', 'Illuminate\Database\ConnectionInterface');
+
+ $this->app->singleton('Illuminate\Database\ConnectionResolverInterface', function () {
+ $resolver = new ConnectionResolver([
+ 'mybb' => $this->app->make('mybb.db'),
+ ]);
+ $resolver->setDefaultConnection('mybb');
+
+ return $resolver;
+ });
+
+ $this->app->alias('Illuminate\Database\ConnectionResolverInterface', 'db');
+
+ $this->app->singleton('MyBB\Core\Database\MigrationRepositoryInterface', function ($app) {
+ return new DatabaseMigrationRepository($app['db'], 'migrations');
+ });
+
+ $this->app->bind(MigrationCreator::class, function (Application $app) {
+ return new MigrationCreator($app->make('Illuminate\Filesystem\Filesystem'), $app->basePath());
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function boot()
+ {
+ if ($this->app->isInstalled()) {
+ AbstractModel::setConnectionResolver($this->app->make('Illuminate\Database\ConnectionResolverInterface'));
+ AbstractModel::setEventDispatcher($this->app->make('events'));
+ }
+ }
+}
diff --git a/app/Database/Migration.php b/app/Database/Migration.php
new file mode 100644
index 00000000..74e9878f
--- /dev/null
+++ b/app/Database/Migration.php
@@ -0,0 +1,114 @@
+ function (Builder $schema) use ($name, $definition) {
+ $schema->create($name, $definition);
+ },
+ 'down' => function (Builder $schema) use ($name) {
+ $schema->drop($name);
+ }
+ ];
+ }
+
+ /**
+ * Rename a table.
+ */
+ public static function renameTable($from, $to)
+ {
+ return [
+ 'up' => function (Builder $schema) use ($from, $to) {
+ $schema->rename($from, $to);
+ },
+ 'down' => function (Builder $schema) use ($from, $to) {
+ $schema->rename($to, $from);
+ }
+ ];
+ }
+
+ /**
+ * Add columns to a table.
+ */
+ public static function addColumns($tableName, array $columnDefinitions)
+ {
+ return [
+ 'up' => function (Builder $schema) use ($tableName, $columnDefinitions) {
+ $schema->table($tableName, function (Blueprint $table) use ($columnDefinitions) {
+ foreach ($columnDefinitions as $columnName => $options) {
+ $type = array_shift($options);
+ $table->addColumn($type, $columnName, $options);
+ }
+ });
+ },
+ 'down' => function (Builder $schema) use ($tableName, $columnDefinitions) {
+ $schema->table($tableName, function (Blueprint $table) use ($columnDefinitions) {
+ $table->dropColumn(array_keys($columnDefinitions));
+ });
+ }
+ ];
+ }
+
+ /**
+ * Rename a column.
+ */
+ public static function renameColumn($tableName, $from, $to)
+ {
+ return [
+ 'up' => function (Builder $schema) use ($tableName, $from, $to) {
+ $schema->table($tableName, function (Blueprint $table) use ($from, $to) {
+ $table->renameColumn($from, $to);
+ });
+ },
+ 'down' => function (Builder $schema) use ($tableName, $from, $to) {
+ $schema->table($tableName, function (Blueprint $table) use ($from, $to) {
+ $table->renameColumn($to, $from);
+ });
+ }
+ ];
+ }
+
+ /**
+ * Add default values for config values.
+ * @param array $defaults
+ * @return array
+ */
+ public static function addSettings(array $defaults)
+ {
+ return [
+ 'up' => function (SettingsRepositoryInterface $settings) use ($defaults) {
+ foreach ($defaults as $key => $value) {
+ $settings->set($key, $value);
+ }
+ },
+ 'down' => function (SettingsRepositoryInterface $settings) use ($defaults) {
+ foreach (array_keys($defaults) as $key) {
+ $settings->delete($key);
+ }
+ }
+ ];
+ }
+}
diff --git a/app/Database/MigrationCreator.php b/app/Database/MigrationCreator.php
new file mode 100644
index 00000000..cf278169
--- /dev/null
+++ b/app/Database/MigrationCreator.php
@@ -0,0 +1,144 @@
+files = $files;
+ $this->publicPath = $publicPath;
+ }
+
+ /**
+ * Create a new migration for the given extension.
+ *
+ * @param string $name
+ * @param Extension $extension
+ * @param string $table
+ * @param bool $create
+ * @return string
+ */
+ public function create($name, $extension = null, $table = null, $create = false)
+ {
+ $migrationPath = $this->getMigrationPath($extension);
+
+ $path = $this->getPath($name, $migrationPath);
+
+ $stub = $this->getStub($table, $create);
+
+ $this->files->put($path, $this->populateStub($stub, $table));
+
+ return $path;
+ }
+
+ /**
+ * Get the migration stub file.
+ *
+ * @param string $table
+ * @param bool $create
+ * @return string
+ */
+ protected function getStub($table, $create)
+ {
+ if (is_null($table)) {
+ return $this->files->get($this->getStubPath().'/blank.stub');
+ }
+
+ // We also have stubs for creating new tables and modifying existing tables
+ // to save the developer some typing when they are creating a new tables
+ // or modifying existing tables. We'll grab the appropriate stub here.
+ $stub = $create ? 'create.stub' : 'update.stub';
+
+ return $this->files->get($this->getStubPath()."/{$stub}");
+ }
+
+ /**
+ * Populate the place-holders in the migration stub.
+ *
+ * @param string $stub
+ * @param string $table
+ * @return string
+ */
+ protected function populateStub($stub, $table)
+ {
+ $replacements = [
+ '{{table}}' => $table
+ ];
+
+ return str_replace(array_keys($replacements), array_values($replacements), $stub);
+ }
+
+ /**
+ * Get the full path name to the migration directory.
+ *
+ * @param string $extension
+ * @return string
+ */
+ protected function getMigrationPath($extension)
+ {
+ $parent = $extension ? public_path().'/extensions/'.$extension : __DIR__.'/../..';
+
+ return $parent.'/migrations';
+ }
+
+ /**
+ * Get the full path name to the migration.
+ *
+ * @param string $name
+ * @param string $path
+ * @return string
+ */
+ protected function getPath($name, $path)
+ {
+ return $path.'/'.$this->getDatePrefix().'_'.$name.'.php';
+ }
+
+ /**
+ * Get the date prefix for the migration.
+ *
+ * @return string
+ */
+ protected function getDatePrefix()
+ {
+ return date('Y_m_d_His');
+ }
+
+ /**
+ * Get the path to the stubs.
+ *
+ * @return string
+ */
+ protected function getStubPath()
+ {
+ return __DIR__.'/../../stubs/migrations';
+ }
+}
diff --git a/app/Database/MigrationRepositoryInterface.php b/app/Database/MigrationRepositoryInterface.php
new file mode 100644
index 00000000..ade0c653
--- /dev/null
+++ b/app/Database/MigrationRepositoryInterface.php
@@ -0,0 +1,60 @@
+files = $files;
+ $this->resolver = $resolver;
+ $this->repository = $repository;
+ }
+
+ /**
+ * Run the outstanding migrations at a given path.
+ *
+ * @param string $path
+ * @param Extension $extension
+ * @return void
+ */
+ public function run($path, Extension $extension = null)
+ {
+ $this->notes = [];
+
+ $files = $this->getMigrationFiles($path);
+
+ $ran = $this->repository->getRan($extension ? $extension->getId() : null);
+
+ $migrations = array_diff($files, $ran);
+
+ $this->runMigrationList($path, $migrations, $extension);
+ }
+
+ /**
+ * Run an array of migrations.
+ *
+ * @param string $path
+ * @param array $migrations
+ * @param Extension $extension
+ * @return void
+ */
+ public function runMigrationList($path, $migrations, Extension $extension = null)
+ {
+ // First we will just make sure that there are any migrations to run. If there
+ // aren't, we will just make a note of it to the developer so they're aware
+ // that all of the migrations have been run against this database system.
+ if (count($migrations) == 0) {
+ $this->note('Nothing to migrate.');
+
+ return;
+ }
+
+ // Once we have the array of migrations, we will spin through them and run the
+ // migrations "up" so the changes are made to the databases. We'll then log
+ // that the migration was run so we don't repeat it next time we execute.
+ foreach ($migrations as $file) {
+ $this->runUp($path, $file, $extension);
+ }
+ }
+
+ /**
+ * Run "up" a migration instance.
+ *
+ * @param string $path
+ * @param string $file
+ * @param string $path
+ * @param Extension $extension
+ * @return void
+ */
+ protected function runUp($path, $file, Extension $extension = null)
+ {
+ $migration = $this->resolve($path, $file);
+
+ $this->runClosureMigration($migration);
+
+ // Once we have run a migrations class, we will log that it was run in this
+ // repository so that we don't try to run it next time we do a migration
+ // in the application. A migration repository keeps the migrate order.
+ $this->repository->log($file, $extension ? $extension->getId() : null);
+
+ $this->note("Migrated: $file");
+ }
+
+ /**
+ * Rolls all of the currently applied migrations back.
+ *
+ * @param string $path
+ * @param Extension $extension
+ * @return int
+ */
+ public function reset($path, Extension $extension = null)
+ {
+ $this->notes = [];
+
+ $migrations = array_reverse($this->repository->getRan($extension->getId()));
+
+ $count = count($migrations);
+
+ if ($count === 0) {
+ $this->note('Nothing to rollback.');
+ } else {
+ foreach ($migrations as $migration) {
+ $this->runDown($path, $migration, $extension);
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Run "down" a migration instance.
+ *
+ * @param $path
+ * @param string $file
+ * @param string $path
+ * @param Extension $extension
+ * @return void
+ */
+ protected function runDown($path, $file, Extension $extension = null)
+ {
+ $migration = $this->resolve($path, $file);
+
+ $this->runClosureMigration($migration, 'down');
+
+ // Once we have successfully run the migration "down" we will remove it from
+ // the migration repository so it will be considered to have not been run
+ // by the application then will be able to fire by any later operation.
+ $this->repository->delete($file, $extension ? $extension->getId() : null);
+
+ $this->note("Rolled back: $file");
+ }
+
+ /**
+ * Runs a closure migration based on the migrate direction.
+ *
+ * @param $migration
+ * @param string $direction
+ * @throws Exception
+ */
+ protected function runClosureMigration($migration, $direction = 'up')
+ {
+ if (is_array($migration) && array_key_exists($direction, $migration)) {
+ app()->call($migration[$direction]);
+ } else {
+ throw new Exception('Migration file should contain an array with up/down.');
+ }
+ }
+
+ /**
+ * Get all of the migration files in a given path.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getMigrationFiles($path)
+ {
+ $files = $this->files->glob($path.'/*_*.php');
+
+ if ($files === false) {
+ return [];
+ }
+
+ $files = array_map(function ($file) {
+ return str_replace('.php', '', basename($file));
+ }, $files);
+
+ // Once we have all of the formatted file names we will sort them and since
+ // they all start with a timestamp this should give us the migrations in
+ // the order they were actually created by the application developers.
+ sort($files);
+
+ return $files;
+ }
+
+ /**
+ * Resolve a migration instance from a file.
+ *
+ * @param string $path
+ * @param string $file
+ * @return array
+ */
+ public function resolve($path, $file)
+ {
+ $migration = "$path/$file.php";
+
+ if ($this->files->exists($migration)) {
+ return $this->files->getRequire($migration);
+ }
+ }
+
+ /**
+ * Raise a note event for the migrator.
+ *
+ * @param string $message
+ * @return void
+ */
+ protected function note($message)
+ {
+ $this->notes[] = $message;
+ }
+
+ /**
+ * Get the notes for the last operation.
+ *
+ * @return array
+ */
+ public function getNotes()
+ {
+ return $this->notes;
+ }
+
+ /**
+ * Resolve the database connection instance.
+ *
+ * @param string $connection
+ * @return \Illuminate\Database\Connection
+ */
+ public function resolveConnection($connection)
+ {
+ return $this->resolver->connection($connection);
+ }
+
+ /**
+ * Set the default connection name.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setConnection($name)
+ {
+ if (! is_null($name)) {
+ $this->resolver->setDefaultConnection($name);
+ }
+
+ $this->repository->setSource($name);
+
+ $this->connection = $name;
+ }
+
+ /**
+ * Get the migration repository instance.
+ *
+ * @return \Illuminate\Database\Migrations\MigrationRepositoryInterface
+ */
+ public function getRepository()
+ {
+ return $this->repository;
+ }
+
+ /**
+ * Determine if the migration repository exists.
+ *
+ * @return bool
+ */
+ public function repositoryExists()
+ {
+ return $this->repository->repositoryExists();
+ }
+
+ /**
+ * Get the file system instance.
+ *
+ * @return \Illuminate\Filesystem\Filesystem
+ */
+ public function getFilesystem()
+ {
+ return $this->files;
+ }
+}
diff --git a/app/Database/Models/AccessToken.php b/app/Database/Models/AccessToken.php
new file mode 100644
index 00000000..426def02
--- /dev/null
+++ b/app/Database/Models/AccessToken.php
@@ -0,0 +1,70 @@
+id = str_random(40);
+ $token->user_id = $userId;
+ $token->last_activity = time();
+ $token->lifetime = $lifetime;
+
+ return $token;
+ }
+
+ public function touch()
+ {
+ $this->last_activity = time();
+
+ return $this->save();
+ }
+
+ /**
+ * Define the relationship with the owner of this access token.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+ */
+ public function user()
+ {
+ return $this->belongsTo(\MyBB\Core\Database\Models\User::class);
+ }
+}
diff --git a/app/Database/Models/AuthToken.php b/app/Database/Models/AuthToken.php
new file mode 100644
index 00000000..1749e670
--- /dev/null
+++ b/app/Database/Models/AuthToken.php
@@ -0,0 +1,97 @@
+id = str_random(40);
+ $token->payload = $payload;
+ $token->created_at = time();
+
+ return $token;
+ }
+
+ /**
+ * Unserialize the payload attribute from the database's JSON value.
+ *
+ * @param string $value
+ * @return string
+ */
+ public function getPayloadAttribute($value)
+ {
+ return json_decode($value, true);
+ }
+
+ /**
+ * Serialize the payload attribute to be stored in the database as JSON.
+ *
+ * @param string $value
+ */
+ public function setPayloadAttribute($value)
+ {
+ $this->attributes['payload'] = json_encode($value);
+ }
+
+ /**
+ * Find the token with the given ID, and assert that it has not expired.
+ *
+ * @param \Illuminate\Database\Eloquent\Builder $query
+ * @param string $id
+ *
+ * @throws InvalidConfirmationTokenException
+ *
+ * @return static
+ */
+ public function scopeValidOrFail($query, $id)
+ {
+ $token = $query->find($id);
+
+ if (! $token || $token->created_at < new DateTime('-1 day')) {
+ throw new InvalidConfirmationTokenException;
+ }
+
+ return $token;
+ }
+}
diff --git a/app/Database/Models/User.php b/app/Database/Models/User.php
index 9ef3cd8e..75d9a19c 100644
--- a/app/Database/Models/User.php
+++ b/app/Database/Models/User.php
@@ -194,4 +194,14 @@ public function getHasher()
{
return $this->hasher;
}
+
+ /**
+ * Define the relationship with the user's access tokens.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\HasMany
+ */
+ public function accessTokens()
+ {
+ return $this->hasMany(\MyBB\Core\Database\Models\AccessToken::class);
+ }
}
diff --git a/app/Events/ConfigureModelDates.php b/app/Events/ConfigureModelDates.php
new file mode 100644
index 00000000..fd85f543
--- /dev/null
+++ b/app/Events/ConfigureModelDates.php
@@ -0,0 +1,48 @@
+model = $modelClass;
+ $this->dates = &$dates;
+ }
+
+ /**
+ * @param string $modelClass
+ * @return bool
+ */
+ public function isModel(string $modelClass)
+ {
+ return $this->modelClass instanceof $modelClass;
+ }
+}
diff --git a/app/Events/ConfigureModelDefaultAttributes.php b/app/Events/ConfigureModelDefaultAttributes.php
new file mode 100644
index 00000000..ddd8f76f
--- /dev/null
+++ b/app/Events/ConfigureModelDefaultAttributes.php
@@ -0,0 +1,44 @@
+modelClass = $modelClass;
+ $this->attributes = &$attributes;
+ }
+
+ /**
+ * @param string $modelClass
+ * @return bool
+ */
+ public function isModel($modelClass)
+ {
+ return $this->modelClass instanceof $modelClass;
+ }
+}
diff --git a/app/Events/ExtensionWasDisabled.php b/app/Events/ExtensionWasDisabled.php
new file mode 100644
index 00000000..7f670ec0
--- /dev/null
+++ b/app/Events/ExtensionWasDisabled.php
@@ -0,0 +1,28 @@
+extension = $extension;
+ }
+}
diff --git a/app/Events/ExtensionWasEnabled.php b/app/Events/ExtensionWasEnabled.php
new file mode 100644
index 00000000..8636c8a4
--- /dev/null
+++ b/app/Events/ExtensionWasEnabled.php
@@ -0,0 +1,28 @@
+extension = $extension;
+ }
+}
diff --git a/app/Events/ExtensionWasUninstalled.php b/app/Events/ExtensionWasUninstalled.php
new file mode 100644
index 00000000..cddb0ee9
--- /dev/null
+++ b/app/Events/ExtensionWasUninstalled.php
@@ -0,0 +1,26 @@
+extension = $extension;
+ }
+}
diff --git a/app/Events/GetModelRelationship.php b/app/Events/GetModelRelationship.php
new file mode 100644
index 00000000..0302d0d9
--- /dev/null
+++ b/app/Events/GetModelRelationship.php
@@ -0,0 +1,49 @@
+model = $model;
+ $this->relationship = $relationship;
+ }
+
+ /**
+ * @param string $model
+ * @param string $relationship
+ * @return bool
+ */
+ public function isRelationship($model, $relationship)
+ {
+ return $this->model instanceof $model && $this->relationship === $relationship;
+ }
+}
diff --git a/app/Exceptions/FloodingException.php b/app/Exceptions/FloodingException.php
new file mode 100644
index 00000000..d79ff5bf
--- /dev/null
+++ b/app/Exceptions/FloodingException.php
@@ -0,0 +1,17 @@
+attributes = $attributes;
+ $this->relationships = $relationships;
+
+ $messages = [implode("\n", $attributes), implode("\n", $relationships)];
+
+ parent::__construct(implode("\n", $messages));
+ }
+
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ public function getRelationships()
+ {
+ return $this->relationships;
+ }
+}
diff --git a/app/Extension/Extension.php b/app/Extension/Extension.php
new file mode 100644
index 00000000..646b173d
--- /dev/null
+++ b/app/Extension/Extension.php
@@ -0,0 +1,269 @@
+path = $path;
+ $this->composerJson = $composerJson;
+ $this->assignId();
+ }
+
+ /**
+ * Assigns the id for the extension used globally.
+ */
+ protected function assignId()
+ {
+ list($vendor, $package) = explode('/', $this->name);
+ $package = str_replace(['mybb-ext-', 'mybb-'], '', $package);
+ $this->id = "$vendor-$package";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __get($name)
+ {
+ return $this->composerJsonAttribute(Str::snake($name, '-'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __isset($name)
+ {
+ return isset($this->{$name}) || $this->composerJsonAttribute(Str::snake($name, '-'));
+ }
+
+ /**
+ * Dot notation getter for composer.json attributes.
+ *
+ * @see https://laravel.com/docs/5.1/helpers#arrays
+ *
+ * @param $name
+ * @return mixed
+ */
+ public function composerJsonAttribute($name)
+ {
+ return Arr::get($this->composerJson, $name);
+ }
+
+ /**
+ * @param bool $installed
+ * @return Extension
+ */
+ public function setInstalled($installed)
+ {
+ $this->installed = $installed;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInstalled()
+ {
+ return $this->installed;
+ }
+
+ /**
+ * @param string $version
+ * @return Extension
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * Loads the icon information from the composer.json.
+ *
+ * @return array|null
+ */
+ public function getIcon()
+ {
+ if (($icon = $this->composerJsonAttribute('extra.mybb-extension.icon'))) {
+ if ($file = Arr::get($icon, 'image')) {
+ $file = $this->path.'/'.$file;
+
+ if (file_exists($file)) {
+ $mimetype = pathinfo($file, PATHINFO_EXTENSION) === 'svg'
+ ? 'image/svg+xml'
+ : finfo_file(finfo_open(FILEINFO_MIME_TYPE), $file);
+ $data = file_get_contents($file);
+
+ $icon['backgroundImage'] = 'url(\'data:'.$mimetype.';base64,'.base64_encode($data).'\')';
+ }
+ }
+
+ return $icon;
+ }
+ }
+
+ /**
+ * @param bool $enabled
+ * @return Extension
+ */
+ public function setEnabled($enabled)
+ {
+ $this->enabled = $enabled;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isEnabled()
+ {
+ return $this->enabled;
+ }
+
+ /**
+ * The raw path of the directory under extensions.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Tests whether the extension has assets.
+ *
+ * @return bool
+ */
+ public function hasAssets()
+ {
+ return realpath($this->path.'/assets/') !== false;
+ }
+
+ /**
+ * Tests whether the extension has migrations.
+ *
+ * @return bool
+ */
+ public function hasMigrations()
+ {
+ return realpath($this->path.'/migrations/') !== false;
+ }
+
+ /**
+ * Generates an array result for the object.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return (array) array_merge([
+ 'id' => $this->getId(),
+ 'version' => $this->getVersion(),
+ 'path' => $this->path,
+ 'icon' => $this->getIcon(),
+ 'hasAssets' => $this->hasAssets(),
+ 'hasMigrations' => $this->hasMigrations(),
+ ], $this->composerJson);
+ }
+}
diff --git a/app/Extension/ExtensionManager.php b/app/Extension/ExtensionManager.php
new file mode 100644
index 00000000..d52da268
--- /dev/null
+++ b/app/Extension/ExtensionManager.php
@@ -0,0 +1,322 @@
+config = $config;
+ $this->app = $app;
+ $this->migrator = $migrator;
+ $this->dispatcher = $dispatcher;
+ $this->filesystem = $filesystem;
+ }
+
+ /**
+ * @return Collection
+ */
+ public function getExtensions()
+ {
+ if (is_null($this->extensions) && $this->filesystem->exists($this->app->basePath().'/vendor/composer/installed.json')) {
+ $extensions = new Collection();
+
+ // Load all packages installed by composer.
+ $installed = json_decode($this->filesystem->get($this->app->basePath().'/vendor/composer/installed.json'), true);
+
+ foreach ($installed as $package) {
+ if (Arr::get($package, 'type') != 'mybb-extension' || empty(Arr::get($package, 'name'))) {
+ continue;
+ }
+ // Instantiates an Extension object using the package path and composer.json file.
+ $extension = new Extension($this->getExtensionsDir().'/'.Arr::get($package, 'name'), $package);
+
+ // Per default all extensions are installed if they are registered in composer.
+ $extension->setInstalled(true);
+ $extension->setVersion(Arr::get($package, 'version'));
+ $extension->setEnabled($this->isEnabled($extension->getId()));
+
+ $extensions->put($extension->getId(), $extension);
+ }
+ $this->extensions = $extensions->sortBy(function ($extension, $name) {
+ return $extension->composerJsonAttribute('extra.mybb-extension.title');
+ });
+ }
+
+ return $this->extensions;
+ }
+
+ /**
+ * Loads an Extension with all information.
+ *
+ * @param string $name
+ * @return Extension|null
+ */
+ public function getExtension($name)
+ {
+ return $this->getExtensions()->get($name);
+ }
+
+ /**
+ * Enables the extension.
+ *
+ * @param string $name
+ */
+ public function enable($name)
+ {
+ if (! $this->isEnabled($name)) {
+ $extension = $this->getExtension($name);
+
+ $enabled = $this->getEnabled();
+
+ $enabled[] = $name;
+
+ $this->migrate($extension);
+
+ $this->publishAssets($extension);
+
+ $this->setEnabled($enabled);
+
+ $extension->setEnabled(true);
+
+ $this->dispatcher->fire(new ExtensionWasEnabled($extension));
+ }
+ }
+
+ /**
+ * Disables an extension.
+ *
+ * @param string $name
+ */
+ public function disable($name)
+ {
+ $enabled = $this->getEnabled();
+
+ if (($k = array_search($name, $enabled)) !== false) {
+ unset($enabled[$k]);
+
+ $extension = $this->getExtension($name);
+
+ $this->setEnabled($enabled);
+
+ $extension->setEnabled(false);
+
+ $this->dispatcher->fire(new ExtensionWasDisabled($extension));
+ }
+ }
+
+ /**
+ * Uninstalls an extension.
+ *
+ * @param string $name
+ */
+ public function uninstall($name)
+ {
+ $extension = $this->getExtension($name);
+
+ $this->disable($name);
+
+ $this->migrateDown($extension);
+
+ $this->unpublishAssets($extension);
+
+ $extension->setInstalled(false);
+
+ $this->dispatcher->fire(new ExtensionWasUninstalled($extension));
+ }
+
+ /**
+ * Copy the assets from an extension's assets directory into public view.
+ *
+ * @param Extension $extension
+ */
+ protected function publishAssets(Extension $extension)
+ {
+ if ($extension->hasAssets()) {
+ $this->filesystem->copyDirectory(
+ $extension->getPath().'/assets',
+ $this->app->publicPath().'/assets/extensions/'.$extension->getId()
+ );
+ }
+ }
+
+ /**
+ * Delete an extension's assets from public view.
+ *
+ * @param Extension $extension
+ */
+ protected function unpublishAssets(Extension $extension)
+ {
+ $this->filesystem->deleteDirectory($this->app->publicPath().'/assets/extensions/'.$extension);
+ }
+
+ /**
+ * Get the path to an extension's published asset.
+ *
+ * @param Extension $extension
+ * @param string $path
+ * @return string
+ */
+ public function getAsset(Extension $extension, string $path)
+ {
+ return $this->app->publicPath().'/assets/extensions/'.$extension->getId().$path;
+ }
+
+ /**
+ * Runs the database migrations for the extension.
+ *
+ * @param Extension $extension
+ * @param bool|true $up
+ */
+ public function migrate(Extension $extension, $up = true)
+ {
+ if ($extension->hasMigrations()) {
+ $migrationDir = $extension->getPath().'/migrations';
+
+ $this->app->bind('Illuminate\Database\Schema\Builder', function ($container) {
+ return $container->make('Illuminate\Database\ConnectionInterface')->getSchemaBuilder();
+ });
+
+ if ($up) {
+ $this->migrator->run($migrationDir, $extension);
+ } else {
+ $this->migrator->reset($migrationDir, $extension);
+ }
+ }
+ }
+
+ /**
+ * Runs the database migrations to reset the database to its old state.
+ *
+ * @param Extension $extension
+ */
+ public function migrateDown(Extension $extension)
+ {
+ $this->migrate($extension, false);
+ }
+
+ /**
+ * The database migrator.
+ *
+ * @return Migrator
+ */
+ public function getMigrator()
+ {
+ return $this->migrator;
+ }
+
+ /**
+ * Get only enabled extensions.
+ *
+ * @return Collection
+ */
+ public function getEnabledExtensions()
+ {
+ return $this->getExtensions()->only($this->getEnabled());
+ }
+
+ /**
+ * Loads all bootstrap.php files of the enabled extensions.
+ *
+ * @return Collection
+ */
+ public function getEnabledBootstrappers()
+ {
+ $bootstrappers = new Collection;
+
+ foreach ($this->getEnabledExtensions() as $extension) {
+ if ($this->filesystem->exists($file = $extension->getPath().'/bootstrap.php')) {
+ $bootstrappers->push($file);
+ }
+ }
+
+ return $bootstrappers;
+ }
+
+ /**
+ * The id's of the enabled extensions.
+ *
+ * @return array
+ */
+ public function getEnabled()
+ {
+ return json_decode($this->config->get('extensions_enabled'), true);
+ }
+
+ /**
+ * Persist the currently enabled extensions.
+ *
+ * @param array $enabled
+ */
+ protected function setEnabled(array $enabled)
+ {
+ $enabled = array_values(array_unique($enabled));
+
+ $this->config->set('extensions_enabled', json_encode($enabled));
+ }
+
+ /**
+ * Whether the extension is enabled.
+ *
+ * @param $extension
+ * @return bool
+ */
+ public function isEnabled($extension)
+ {
+ return in_array($extension, $this->getEnabled());
+ }
+
+ /**
+ * The extensions path.
+ *
+ * @return string
+ */
+ protected function getExtensionsDir()
+ {
+ return $this->app->basePath().'/vendor';
+ }
+}
diff --git a/app/Extension/ExtensionServiceProvider.php b/app/Extension/ExtensionServiceProvider.php
new file mode 100644
index 00000000..a571b7ec
--- /dev/null
+++ b/app/Extension/ExtensionServiceProvider.php
@@ -0,0 +1,31 @@
+app->bind('mybb.extensions', \MyBB\Core\Extension\ExtensionManager::class);
+
+ $bootstrappers = $this->app->make('mybb.extensions')->getEnabledBootstrappers();
+
+ foreach ($bootstrappers as $file) {
+ $bootstrapper = require $file;
+
+ $this->app->call($bootstrapper);
+ }
+ }
+}
diff --git a/app/Forum/UrlGenerator.php b/app/Forum/UrlGenerator.php
new file mode 100644
index 00000000..4bc45aef
--- /dev/null
+++ b/app/Forum/UrlGenerator.php
@@ -0,0 +1,16 @@
+listen();
+ }
+
+ /**
+ * Use as PSR-7 middleware.
+ *
+ * @param ServerRequestInterface $request
+ * @param ResponseInterface $response
+ * @param callable|null $out
+ * @return ResponseInterface
+ */
+ public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $out = null)
+ {
+ $app = $this->getApp();
+
+ $this->collectGarbage($app);
+
+ $middleware = $this->getMiddleware($app);
+
+ return $middleware($request, $response, $out);
+ }
+
+ /**
+ * @param Application $app
+ * @return MiddlewareInterface
+ */
+ abstract protected function getMiddleware(Application $app);
+
+ private function collectGarbage()
+ {
+ if ($this->hitsLottery()) {
+ //todo: Implement EmailToken, and PasswordToken models
+ AccessToken::whereRaw('last_activity <= ? - lifetime', [time()])->delete();
+
+ $earliestToKeep = date('Y-m-d H:i:s', time() - 24 * 60 * 60);
+ }
+ }
+
+ private function hitsLottery()
+ {
+ return mt_rand(1, 100) <= 2;
+ }
+}
\ No newline at end of file
diff --git a/app/Http/AbstractUrlGenerator.php b/app/Http/AbstractUrlGenerator.php
new file mode 100644
index 00000000..1306ab52
--- /dev/null
+++ b/app/Http/AbstractUrlGenerator.php
@@ -0,0 +1,76 @@
+app = $app;
+ $this->routes = $routes;
+ }
+
+ /**
+ * Generate a URL to a named route.
+ *
+ * @param string $name
+ * @param array $parameters
+ * @return string
+ */
+ public function toRoute($name, $parameters = [])
+ {
+ $path = $this->routes->getPath($name, $parameters);
+ $path = ltrim($path, '/');
+
+ return $this->toBase().'/'.$path;
+ }
+
+ /**
+ * Generate a URL to a path.
+ *
+ * @param string $path
+ * @return string
+ */
+ public function toPath($path)
+ {
+ return $this->toBase().'/'.$path;
+ }
+
+ /**
+ * Generate a URL to base with UrlGenerator's prefix.
+ *
+ * @return string
+ */
+ public function toBase()
+ {
+ return $this->app->url($this->path);
+ }
+}
diff --git a/app/Http/Controller/AbstractHtmlController.php b/app/Http/Controller/AbstractHtmlController.php
new file mode 100644
index 00000000..843ecacc
--- /dev/null
+++ b/app/Http/Controller/AbstractHtmlController.php
@@ -0,0 +1,36 @@
+render($request);
+
+ $response = new Response;
+ $response->getBody()->write($view);
+
+ return $response;
+ }
+
+ /**
+ * @param Request $request
+ * @return \Illuminate\Contracts\Support\Renderable
+ */
+ abstract protected function render(Request $request);
+}
\ No newline at end of file
diff --git a/app/Http/Controller/ControllerInterface.php b/app/Http/Controller/ControllerInterface.php
new file mode 100644
index 00000000..9349e2d0
--- /dev/null
+++ b/app/Http/Controller/ControllerInterface.php
@@ -0,0 +1,21 @@
+basePath = $basePath;
+ $this->publicPath = $publicPath;
+
+ if (file_exists($file = $this->basePath.'/config.php')) {
+ $this->config = include $file;
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getBasePath()
+ {
+ return $this->basePath;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublicPath()
+ {
+ return $this->publicPath;
+ }
+
+ /**
+ * @return string
+ */
+ public function getStoragePath()
+ {
+ return $this->storagePath;
+ }
+
+ /**
+ * @param $basePath
+ */
+ public function setBasePath($basePath)
+ {
+ $this->basePath = $basePath;
+ }
+
+ /**
+ * @param $publicPath
+ */
+ public function setPublicPath($publicPath)
+ {
+ $this->publicPath = $publicPath;
+ }
+
+ /**
+ * @param $storagePath
+ */
+ public function setStoragePath($storagePath)
+ {
+ $this->storagePath = $storagePath;
+ }
+
+ /**
+ * @return array
+ */
+ public function getConfig()
+ {
+ return $this->config;
+ }
+
+ /**
+ * @param array $config
+ */
+ public function setConfig(array $config)
+ {
+ $this->config = $config;
+ }
+
+ /**
+ * @param callable $callback
+ */
+ public function extend(callable $callback)
+ {
+ $this->extendCallbacks[] = $callback;
+ }
+
+ /**
+ * @return Application
+ */
+ public function getApp()
+ {
+ if ($this->app !== null) {
+ return $this->app;
+ }
+
+ date_default_timezone_set('UTC');
+
+ $app = new Application($this->basePath, $this->publicPath);
+
+ if ($this->storagePath) {
+ $app->useStoragePath($this->storagePath);
+ }
+
+ $app->instance('env', 'production');
+ $app->instance('mybb.config', $this->config);
+ $app->instance('config', $config = $this->getIlluminateConfig($app));
+
+ $this->registerLogger($app);
+
+ $this->registerCache($app);
+
+ //todo: Register Local, Twig, Database, and other needed providors here
+ $app->register(\MyBB\Core\Settings\SettingsRepositoryInterface::class);
+ $app->register('Illuminate\Bus\BusServiceProvider');
+ $app->register('Illuminate\Filesystem\FilesystemServiceProvider');
+ $app->register('Illuminate\Hashing\HashServiceProvider');
+ $app->register('Illuminate\Mail\MailServiceProvider');
+ $app->register('Illuminate\View\ViewServiceProvider');
+ $app->register('Illuminate\Validation\ValidationServiceProvider');
+
+ if ($app->isInstalled() && $app->isUpToDate()) {
+ $settings = $app->make('MyBB\Core\Settings\SettingsRepositoryInterface');
+
+ $config->set('mail.driver', $settings->get('mail_driver'));
+ $config->set('mail.host', $settings->get('mail_host'));
+ $config->set('mail.port', $settings->get('mail_port'));
+ $config->set('mail.from.address', $settings->get('mail_from'));
+ $config->set('mail.from.name', $settings->get('forum_title'));
+ $config->set('mail.encryption', $settings->get('mail_encryption'));
+ $config->set('mail.username', $settings->get('mail_username'));
+ $config->set('mail.password', $settings->get('mail_password'));
+
+ //todo: Register API, Forum, and Admin ServiceProviders here
+
+ foreach ($this->extendCallbacks as $callback) {
+ $app->call($callback);
+ }
+
+ //register the ExtensionServiceProvider
+ $app->register(\MyBB\Core\Extension\ExtensionServiceProvider::class);
+ }
+
+ $app->boot();
+
+ $this->app = $app;
+
+ return $app;
+ }
+
+ /**
+ * @param Application $app
+ * @return ConfigRepository
+ */
+ protected function getIlluminateConfig(Application $app)
+ {
+ return new ConfigRepository([
+ 'view' => [
+ 'paths' => [],
+ 'compiled' => $app->storagePath().'/views',
+ ],
+ 'mail' => [
+ 'driver' => 'mail',
+ ],
+ 'filesystems' => [
+ 'default' => 'local',
+ 'cloud' => 's3',
+ 'disks' => [
+ 'mybb-avatars' => [
+ 'driver' => 'local',
+ 'root' => $app->publicPath().'/assets/avatars'
+ ]
+ ]
+ ]
+ ]);
+ }
+
+ /**
+ * @param Application $app
+ */
+ protected function registerLogger(Application $app)
+ {
+ $logger = new Logger($app->environment());
+ $logPath = $app->storagePath().'/logs/mybb.log';
+
+ $handler = new StreamHandler($logPath, Logger::DEBUG);
+ $handler->setFormatter(new LineFormatter(null, null, true, true));
+
+ $logger->pushHandler($handler);
+
+ $app->instance('log', $logger);
+ $app->alias('log', 'Psr\Log\LoggerInterface');
+ }
+
+ /**
+ * @param Application $app
+ */
+ protected function registerCache(Application $app)
+ {
+ $app->singleton('cache.store', function ($app) {
+ return new \Illuminate\Cache\Repository($app->make('cache.filestore'));
+ });
+
+ $app->singleton('cache.filestore', function ($app) {
+ return new \Illuminate\Cache\FileStore(
+ new \Illuminate\Filesystem\Filesystem(),
+ $app->storagePath().'/cache'
+ );
+ });
+
+ $app->alias('cache.filestore', 'Illuminate\Contracts\Cache\Store');
+ $app->alias('cache.store', 'Illuminate\Contracts\Cache\Repository');
+ }
+}
diff --git a/app/Kernel/AbstractServiceProvider.php b/app/Kernel/AbstractServiceProvider.php
new file mode 100644
index 00000000..210a6982
--- /dev/null
+++ b/app/Kernel/AbstractServiceProvider.php
@@ -0,0 +1,35 @@
+registerBaseBindings();
+
+ $this->registerBaseServiceProviders();
+
+ $this->registerCoreContainerAliases();
+
+ if ($basePath) {
+ $this->setBasePath($basePath);
+ }
+
+ if ($publicPath) {
+ $this->setPublicPath($publicPath);
+ }
+ }
+
+ /**
+ * Determine if MyBB has been installed.
+ *
+ * @return bool
+ */
+ public function isInstalled()
+ {
+ return $this->bound('mybb.config');
+ }
+
+ public function isUpToDate()
+ {
+ $settings = $this->make('MyBB\Core\Settings\SettingsRepositoryInterface');
+
+ try {
+ $version = $settings->get('version');
+ } finally {
+ $isUpToDate = isset($version) && $version === $this->version();
+ }
+
+ return $isUpToDate;
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function config($key, $default = null)
+ {
+ return array_get($this->make('mybb.config'), $key, $default);
+ }
+
+ /**
+ * Check if MyBB is in debug mode.
+ *
+ * @return bool
+ */
+ public function inDebugMode()
+ {
+ return ! $this->isInstalled() || $this->config('debug');
+ }
+
+ /**
+ * Get the URL to the MyBB installation.
+ *
+ * @param string $path
+ * @return string
+ */
+ public function url($path = null)
+ {
+ $config = $this->isInstalled() ? $this->make('mybb.config') : [];
+ $url = array_get($config, 'url', array_get($_SERVER, 'REQUEST_URI'));
+
+ if (is_array($url)) {
+ if (isset($url[$path])) {
+ return $url[$path];
+ }
+
+ $url = $url['base'];
+ }
+
+ if ($path) {
+ $url .= '/'.array_get($config, "paths.$path", $path);
+ }
+
+ return $url;
+ }
+
+ /**
+ * Get the version number of the application.
+ *
+ * @return string
+ */
+ public function version()
+ {
+ return static::VERSION;
+ }
+
+ /**
+ * Register the basic bindings into the container.
+ */
+ protected function registerBaseBindings()
+ {
+ static::setInstance($this);
+
+ $this->instance('app', $this);
+
+ $this->instance('Illuminate\Container\Container', $this);
+ }
+
+ /**
+ * Register all of the base service providers.
+ */
+ protected function registerBaseServiceProviders()
+ {
+ $this->register(new EventServiceProvider($this));
+ }
+
+ /**
+ * Set the base path for the application.
+ *
+ * @param string $basePath
+ * @return $this
+ */
+ public function setBasePath($basePath)
+ {
+ $this->basePath = rtrim($basePath, '\/');
+
+ $this->bindPathsInContainer();
+
+ return $this;
+ }
+
+ /**
+ * Set the public path for the application.
+ *
+ * @param string $publicPath
+ * @return $this
+ */
+ public function setPublicPath($publicPath)
+ {
+ $this->publicPath = rtrim($publicPath, '\/');
+
+ $this->bindPathsInContainer();
+
+ return $this;
+ }
+
+ /**
+ * Bind all of the application paths in the container.
+ *
+ * @return void
+ */
+ protected function bindPathsInContainer()
+ {
+ foreach (['base', 'public', 'storage'] as $path) {
+ $this->instance('path.'.$path, $this->{$path.'Path'}());
+ }
+ }
+
+ /**
+ * Get the base path of the Laravel installation.
+ *
+ * @return string
+ */
+ public function basePath()
+ {
+ return $this->basePath;
+ }
+
+ /**
+ * Get the path to the public / web directory.
+ *
+ * @return string
+ */
+ public function publicPath()
+ {
+ return $this->publicPath;
+ }
+
+ /**
+ * Get the path to the storage directory.
+ *
+ * @return string
+ */
+ public function storagePath()
+ {
+ return $this->storagePath ?: $this->basePath.DIRECTORY_SEPARATOR.'storage';
+ }
+
+ /**
+ * Set the storage directory.
+ *
+ * @param string $path
+ * @return $this
+ */
+ public function useStoragePath($path)
+ {
+ $this->storagePath = $path;
+
+ $this->instance('path.storage', $path);
+
+ return $this;
+ }
+
+ /**
+ * Get or check the current application environment.
+ *
+ * @param mixed
+ * @return string
+ */
+ public function environment()
+ {
+ if (func_num_args() > 0) {
+ $patterns = is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args();
+
+ foreach ($patterns as $pattern) {
+ if (Str::is($pattern, $this['env'])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return $this['env'];
+ }
+
+ /**
+ * Determine if we are running in the console.
+ *
+ * @return bool
+ */
+ public function runningInConsole()
+ {
+ return php_sapi_name() == 'cli';
+ }
+
+ /**
+ * Determine if we are running unit tests.
+ *
+ * @return bool
+ */
+ public function runningUnitTests()
+ {
+ return $this['env'] == 'testing';
+ }
+
+ /**
+ * Register all of the configured providers.
+ *
+ * @return void
+ */
+ public function registerConfiguredProviders()
+ {
+ }
+
+ /**
+ * Register a service provider with the application.
+ *
+ * @param ServiceProvider|string $provider
+ * @param array $options
+ * @param bool $force
+ * @return ServiceProvider
+ */
+ public function register($provider, $options = [], $force = false)
+ {
+ if ($registered = $this->getProvider($provider) && ! $force) {
+ return $registered;
+ }
+
+ // If the given "provider" is a string, we will resolve it, passing in the
+ // application instance automatically for the developer. This is simply
+ // a more convenient way of specifying your service provider classes.
+ if (is_string($provider)) {
+ $provider = $this->resolveProviderClass($provider);
+ }
+
+ $provider->register();
+
+ // Once we have registered the service we will iterate through the options
+ // and set each of them on the application so they will be available on
+ // the actual loading of the service objects and for developer usage.
+ foreach ($options as $key => $value) {
+ $this[$key] = $value;
+ }
+
+ $this->markAsRegistered($provider);
+
+ // If the application has already booted, we will call this boot method on
+ // the provider class so it has an opportunity to do its boot logic and
+ // will be ready for any usage by the developer's application logics.
+ if ($this->booted) {
+ $this->bootProvider($provider);
+ }
+
+ return $provider;
+ }
+
+ /**
+ * Get the registered service provider instance if it exists.
+ *
+ * @param ServiceProvider|string $provider
+ * @return ServiceProvider|null
+ */
+ public function getProvider($provider)
+ {
+ $name = is_string($provider) ? $provider : get_class($provider);
+
+ return Arr::first($this->serviceProviders, function ($key, $value) use ($name) {
+ return $value instanceof $name;
+ });
+ }
+
+ /**
+ * Resolve a service provider instance from the class name.
+ *
+ * @param string $provider
+ * @return ServiceProvider
+ */
+ public function resolveProviderClass($provider)
+ {
+ return new $provider($this);
+ }
+
+ /**
+ * Mark the given provider as registered.
+ *
+ * @param ServiceProvider $provider
+ * @return void
+ */
+ protected function markAsRegistered($provider)
+ {
+ $this['events']->fire($class = get_class($provider), [$provider]);
+
+ $this->serviceProviders[] = $provider;
+
+ $this->loadedProviders[$class] = true;
+ }
+
+ /**
+ * Load and boot all of the remaining deferred providers.
+ */
+ public function loadDeferredProviders()
+ {
+ // We will simply spin through each of the deferred providers and register each
+ // one and boot them if the application has booted. This should make each of
+ // the remaining services available to this application for immediate use.
+ foreach ($this->deferredServices as $service => $provider) {
+ $this->loadDeferredProvider($service);
+ }
+
+ $this->deferredServices = [];
+ }
+
+ /**
+ * Load the provider for a deferred service.
+ *
+ * @param string $service
+ */
+ public function loadDeferredProvider($service)
+ {
+ if (! isset($this->deferredServices[$service])) {
+ return;
+ }
+
+ $provider = $this->deferredServices[$service];
+
+ // If the service provider has not already been loaded and registered we can
+ // register it with the application and remove the service from this list
+ // of deferred services, since it will already be loaded on subsequent.
+ if (! isset($this->loadedProviders[$provider])) {
+ $this->registerDeferredProvider($provider, $service);
+ }
+ }
+
+ /**
+ * Register a deferred provider and service.
+ *
+ * @param string $provider
+ * @param string $service
+ */
+ public function registerDeferredProvider($provider, $service = null)
+ {
+ // Once the provider that provides the deferred service has been registered we
+ // will remove it from our local list of the deferred services with related
+ // providers so that this container does not try to resolve it out again.
+ if ($service) {
+ unset($this->deferredServices[$service]);
+ }
+
+ $this->register($instance = new $provider($this));
+
+ if (! $this->booted) {
+ $this->booting(function () use ($instance) {
+ $this->bootProvider($instance);
+ });
+ }
+ }
+
+ /**
+ * Resolve the given type from the container.
+ *
+ * (Overriding Container::make)
+ *
+ * @param string $abstract
+ * @param array $parameters
+ * @return mixed
+ */
+ public function make($abstract, array $parameters = [])
+ {
+ $abstract = $this->getAlias($abstract);
+
+ if (isset($this->deferredServices[$abstract])) {
+ $this->loadDeferredProvider($abstract);
+ }
+
+ return parent::make($abstract, $parameters);
+ }
+
+ /**
+ * Determine if the given abstract type has been bound.
+ *
+ * (Overriding Container::bound)
+ *
+ * @param string $abstract
+ * @return bool
+ */
+ public function bound($abstract)
+ {
+ return isset($this->deferredServices[$abstract]) || parent::bound($abstract);
+ }
+
+ /**
+ * Determine if the application has booted.
+ *
+ * @return bool
+ */
+ public function isBooted()
+ {
+ return $this->booted;
+ }
+
+ /**
+ * Boot the application's service providers.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ if ($this->booted) {
+ return;
+ }
+
+ // Once the application has booted we will also fire some "booted" callbacks
+ // for any listeners that need to do work after this initial booting gets
+ // finished. This is useful when ordering the boot-up processes we run.
+ $this->fireAppCallbacks($this->bootingCallbacks);
+
+ array_walk($this->serviceProviders, function ($p) {
+ $this->bootProvider($p);
+ });
+
+ $this->booted = true;
+
+ $this->fireAppCallbacks($this->bootedCallbacks);
+ }
+
+ /**
+ * Boot the given service provider.
+ *
+ * @param ServiceProvider $provider
+ * @return mixed
+ */
+ protected function bootProvider(ServiceProvider $provider)
+ {
+ if (method_exists($provider, 'boot')) {
+ return $this->call([$provider, 'boot']);
+ }
+ }
+
+ /**
+ * Register a new boot listener.
+ *
+ * @param mixed $callback
+ * @return void
+ */
+ public function booting($callback)
+ {
+ $this->bootingCallbacks[] = $callback;
+ }
+
+ /**
+ * Register a new "booted" listener.
+ *
+ * @param mixed $callback
+ * @return void
+ */
+ public function booted($callback)
+ {
+ $this->bootedCallbacks[] = $callback;
+
+ if ($this->isBooted()) {
+ $this->fireAppCallbacks([$callback]);
+ }
+ }
+
+ /**
+ * Call the booting callbacks for the application.
+ *
+ * @param array $callbacks
+ * @return void
+ */
+ protected function fireAppCallbacks(array $callbacks)
+ {
+ foreach ($callbacks as $callback) {
+ call_user_func($callback, $this);
+ }
+ }
+
+ /**
+ * Get the path to the cached "compiled.php" file.
+ *
+ * @return string
+ */
+ public function getCachedCompilePath()
+ {
+ return $this->basePath().'/bootstrap/cache/compiled.php';
+ }
+
+ /**
+ * Get the path to the cached services.json file.
+ *
+ * @return string
+ */
+ public function getCachedServicesPath()
+ {
+ return $this->basePath().'/bootstrap/cache/services.json';
+ }
+
+ /**
+ * Determine if the application is currently down for maintenance.
+ *
+ * @return bool
+ */
+ public function isDownForMaintenance()
+ {
+ return $this->config('offline');
+ }
+
+ /**
+ * Get the service providers that have been loaded.
+ *
+ * @return array
+ */
+ public function getLoadedProviders()
+ {
+ return $this->loadedProviders;
+ }
+
+ /**
+ * Get the application's deferred services.
+ *
+ * @return array
+ */
+ public function getDeferredServices()
+ {
+ return $this->deferredServices;
+ }
+
+ /**
+ * Set the application's deferred services.
+ *
+ * @param array $services
+ * @return void
+ */
+ public function setDeferredServices(array $services)
+ {
+ $this->deferredServices = $services;
+ }
+
+ /**
+ * Add an array of services to the application's deferred services.
+ *
+ * @param array $services
+ * @return void
+ */
+ public function addDeferredServices(array $services)
+ {
+ $this->deferredServices = array_merge($this->deferredServices, $services);
+ }
+
+ /**
+ * Determine if the given service is a deferred service.
+ *
+ * @param string $service
+ * @return bool
+ */
+ public function isDeferredService($service)
+ {
+ return isset($this->deferredServices[$service]);
+ }
+
+ /**
+ * Register the core class aliases in the container.
+ */
+ public function registerCoreContainerAliases()
+ {
+ $aliases = [
+ 'app' => ['MyBB\Core\Kernel\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'],
+ 'blade.compiler' => 'Illuminate\View\Compilers\BladeCompiler',
+ 'cache' => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
+ 'cache.store' => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'],
+ 'config' => ['Illuminate\Config\Repository', 'Illuminate\Contracts\Config\Repository'],
+ 'db' => 'Illuminate\Database\DatabaseManager',
+ 'events' => ['Illuminate\Events\Dispatcher', 'Illuminate\Contracts\Events\Dispatcher'],
+ 'files' => 'Illuminate\Filesystem\Filesystem',
+ 'filesystem' => ['Illuminate\Filesystem\FilesystemManager', 'Illuminate\Contracts\Filesystem\Factory'],
+ 'filesystem.disk' => 'Illuminate\Contracts\Filesystem\Filesystem',
+ 'filesystem.cloud' => 'Illuminate\Contracts\Filesystem\Cloud',
+ 'hash' => 'Illuminate\Contracts\Hashing\Hasher',
+ 'mailer' => ['Illuminate\Mail\Mailer', 'Illuminate\Contracts\Mail\Mailer', 'Illuminate\Contracts\Mail\MailQueue'],
+ 'validator' => ['Illuminate\Validation\Factory', 'Illuminate\Contracts\Validation\Factory'],
+ 'view' => ['Illuminate\View\Factory', 'Illuminate\Contracts\View\Factory'],
+ ];
+
+ foreach ($aliases as $key => $aliases) {
+ foreach ((array) $aliases as $alias) {
+ $this->alias($key, $alias);
+ }
+ }
+ }
+
+ /**
+ * Flush the container of all bindings and resolved instances.
+ */
+ public function flush()
+ {
+ parent::flush();
+
+ $this->loadedProviders = [];
+ }
+}
diff --git a/app/Settings/DatabaseSettingsRepository.php b/app/Settings/DatabaseSettingsRepository.php
new file mode 100644
index 00000000..5c1e4071
--- /dev/null
+++ b/app/Settings/DatabaseSettingsRepository.php
@@ -0,0 +1,50 @@
+database = $connection;
+ }
+
+ public function all()
+ {
+ return $this->database->table('settings')->lists('value', 'key');
+ }
+
+ public function get($key, $default = null)
+ {
+ if (is_null($value = $this->database->table('settings')->where('key', $key)->value('value'))) {
+ return $default;
+ }
+
+ return $value;
+ }
+
+ public function set($key, $value)
+ {
+ $query = $this->database->table('settings')->where('key', $key);
+
+ $method = $query->exists() ? 'update' : 'insert';
+
+ $query->$method(compact('key', 'value'));
+ }
+
+ public function delete($keyLike)
+ {
+ $this->database->table('settings')->where('key', 'like', $keyLike)->delete();
+ }
+}
diff --git a/app/Settings/MemoryCacheSettingsRepository.php b/app/Settings/MemoryCacheSettingsRepository.php
new file mode 100644
index 00000000..9d7c3b3b
--- /dev/null
+++ b/app/Settings/MemoryCacheSettingsRepository.php
@@ -0,0 +1,59 @@
+inner = $inner;
+ }
+
+ public function all()
+ {
+ if (! $this->isCached) {
+ $this->cache = $this->inner->all();
+ $this->isCached = true;
+ }
+
+ return $this->cache;
+ }
+
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->cache)) {
+ return $this->cache[$key];
+ } elseif (! $this->isCached) {
+ return array_get($this->all(), $key, $default);
+ }
+
+ return $default;
+ }
+
+ public function set($key, $value)
+ {
+ $this->cache[$key] = $value;
+
+ $this->inner->set($key, $value);
+ }
+
+ public function delete($key)
+ {
+ unset($this->cache[$key]);
+
+ $this->inner->delete($key);
+ }
+}
diff --git a/app/Settings/SettingsRepositoryInterface.php b/app/Settings/SettingsRepositoryInterface.php
new file mode 100644
index 00000000..ac866211
--- /dev/null
+++ b/app/Settings/SettingsRepositoryInterface.php
@@ -0,0 +1,21 @@
+app->singleton('MyBB\Settings\SettingsRepositoryInterface', function () {
+ return new MemoryCacheSettingsRepository(
+ new DatabaseSettingsRepository(
+ $this->app->make('Illuminate\Database\ConnectionInterface')
+ )
+ );
+ });
+
+ $this->app->alias('MyBB\Settings\SettingsRepositoryInterface', 'mybb.settings');
+ }
+}
diff --git a/composer.json b/composer.json
index 6b5b180a..7ec46f47 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,11 @@
"mccool/laravel-auto-presenter": "^4.2",
"league/fractal": "^0.13.0",
"davejamesmiller/laravel-breadcrumbs": "^3.0",
- "doctrine/dbal": "^2.5"
+ "doctrine/dbal": "^2.5",
+ "monolog/monolog": "^1.22",
+ "psr/http-message": "^1.0",
+ "zendframework/zend-diactoros": "^1.3",
+ "zendframework/zend-stratigility": "^1.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
diff --git a/composer.lock b/composer.lock
index e44b0b87..e2cc6370 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "cf12f09e682d04250c038ad11e9b2d0b",
- "content-hash": "d89e922c1957917cf6da5549bdfd1fd8",
+ "hash": "0169595fa3fe89000a7d9df59e4152e3",
+ "content-hash": "6b10be36c9a170d475019f725df37afa",
"packages": [
{
"name": "classpreloader/classpreloader",
@@ -664,6 +664,58 @@
],
"time": "2016-02-17 12:41:57"
},
+ {
+ "name": "http-interop/http-middleware",
+ "version": "0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/http-interop/http-middleware.git",
+ "reference": "ff545c87e97bf4d88f0cb7eb3e89f99aaa53d7a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/http-interop/http-middleware/zipball/ff545c87e97bf4d88f0cb7eb3e89f99aaa53d7a9",
+ "reference": "ff545c87e97bf4d88f0cb7eb3e89f99aaa53d7a9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Interop\\Http\\Middleware\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP middleware",
+ "keywords": [
+ "factory",
+ "http",
+ "middleware",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-09-25 13:30:27"
+ },
{
"name": "jakub-onderka/php-console-color",
"version": "0.1",
@@ -1729,6 +1781,56 @@
],
"time": "2016-03-18 20:34:03"
},
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06 14:39:51"
+ },
{
"name": "psr/log",
"version": "1.0.2",
@@ -2915,6 +3017,153 @@
"environment"
],
"time": "2016-09-01 10:05:43"
+ },
+ {
+ "name": "zendframework/zend-diactoros",
+ "version": "1.3.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-diactoros.git",
+ "reference": "969ff423d3f201da3ff718a5831bb999bb0669b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/969ff423d3f201da3ff718a5831bb999bb0669b0",
+ "reference": "969ff423d3f201da3ff718a5831bb999bb0669b0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4 || ^7.0",
+ "psr/http-message": "~1.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "~1.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6 || ^5.5",
+ "squizlabs/php_codesniffer": "^2.3.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3-dev",
+ "dev-develop": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Diactoros\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "description": "PSR HTTP Message implementations",
+ "homepage": "https://github.com/zendframework/zend-diactoros",
+ "keywords": [
+ "http",
+ "psr",
+ "psr-7"
+ ],
+ "time": "2016-10-11 13:25:21"
+ },
+ {
+ "name": "zendframework/zend-escaper",
+ "version": "2.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-escaper.git",
+ "reference": "2dcd14b61a72d8b8e27d579c6344e12c26141d4e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/2dcd14b61a72d8b8e27d579c6344e12c26141d4e",
+ "reference": "2dcd14b61a72d8b8e27d579c6344e12c26141d4e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "1.7.*",
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev",
+ "dev-develop": "2.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Escaper\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "homepage": "https://github.com/zendframework/zend-escaper",
+ "keywords": [
+ "escaper",
+ "zf2"
+ ],
+ "time": "2016-06-30 19:48:38"
+ },
+ {
+ "name": "zendframework/zend-stratigility",
+ "version": "1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-stratigility.git",
+ "reference": "c410d367bb85f0a3cca44f112957d0ee28895d19"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/c410d367bb85f0a3cca44f112957d0ee28895d19",
+ "reference": "c410d367bb85f0a3cca44f112957d0ee28895d19",
+ "shasum": ""
+ },
+ "require": {
+ "http-interop/http-middleware": "^0.2.0",
+ "php": "^5.6 || ^7.0",
+ "psr/http-message": "^1.0",
+ "zendframework/zend-escaper": "^2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.7 || ^5.5",
+ "squizlabs/php_codesniffer": "^2.6.2",
+ "zendframework/zend-diactoros": "^1.0"
+ },
+ "suggest": {
+ "psr/http-message-implementation": "Please install a psr/http-message-implementation to consume Stratigility; e.g., zendframework/zend-diactoros"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.3.0-dev",
+ "dev-develop": "2.0.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Stratigility\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Middleware for PHP",
+ "homepage": "https://github.com/zendframework/zend-stratigility",
+ "keywords": [
+ "http",
+ "middleware",
+ "psr-7"
+ ],
+ "time": "2016-11-11 00:16:05"
}
],
"packages-dev": [
diff --git a/database/migrations/2016_12_17_013923_create_access_tokens_table.php b/database/migrations/2016_12_17_013923_create_access_tokens_table.php
new file mode 100644
index 00000000..08a12374
--- /dev/null
+++ b/database/migrations/2016_12_17_013923_create_access_tokens_table.php
@@ -0,0 +1,32 @@
+string('id', 100)->primary();
+ $table->integer('user_id')->unsigned();
+ $table->timestamp('created_at');
+ $table->timestamp('expires_at');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('access_tokens');
+ }
+}
diff --git a/database/migrations/2016_12_17_014105_create_api_keys_table.php b/database/migrations/2016_12_17_014105_create_api_keys_table.php
new file mode 100644
index 00000000..1e88be55
--- /dev/null
+++ b/database/migrations/2016_12_17_014105_create_api_keys_table.php
@@ -0,0 +1,29 @@
+string('id', 100)->primary();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('api_keys');
+ }
+}
diff --git a/database/migrations/2016_12_17_014247_create_config_table.php b/database/migrations/2016_12_17_014247_create_config_table.php
new file mode 100644
index 00000000..7165c836
--- /dev/null
+++ b/database/migrations/2016_12_17_014247_create_config_table.php
@@ -0,0 +1,30 @@
+string('key', 100)->primary();
+ $table->binary('value')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('config');
+ }
+}
diff --git a/database/migrations/2016_12_17_014458_create_auth_tokens_table.php b/database/migrations/2016_12_17_014458_create_auth_tokens_table.php
new file mode 100644
index 00000000..c33d94b3
--- /dev/null
+++ b/database/migrations/2016_12_17_014458_create_auth_tokens_table.php
@@ -0,0 +1,31 @@
+string('id', 100)->primary();
+ $table->string('payload', 150);
+ $table->timestamp('created_at');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('auth_tokens');
+ }
+}
diff --git a/database/migrations/2016_12_17_014559_create_email_tokens_table.php b/database/migrations/2016_12_17_014559_create_email_tokens_table.php
new file mode 100644
index 00000000..cc867688
--- /dev/null
+++ b/database/migrations/2016_12_17_014559_create_email_tokens_table.php
@@ -0,0 +1,32 @@
+string('id', 100)->primary();
+ $table->string('email', 150);
+ $table->integer('user_id')->unsigned();
+ $table->timestamp('created_at');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::drop('email_tokens');
+ }
+}