diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..2125666142 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..e0d0d37db7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/bootstrap/compiled.php +/vendor +composer.phar +composer.lock +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..015febc403 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contribution Guidelines + +Please submit all issues and pull requests to the [laravel/framework](http://github.com/laravel/framework) repository! \ No newline at end of file diff --git a/app/breadcrumbs.php b/app/breadcrumbs.php new file mode 100644 index 0000000000..85d8cb8586 --- /dev/null +++ b/app/breadcrumbs.php @@ -0,0 +1,73 @@ +push(Lang::get('lrs.list'), url('/lrs')); +}); +Breadcrumbs::register('lrs.users', function($breadcrumbs, $lrs) { + $breadcrumbs->push(Lang::get('lrs.sidebar.users'), url('/lrs/' . $lrs->_id . '/users')); +}); +Breadcrumbs::register('create', function($breadcrumbs) { + $breadcrumbs->parent('lrs'); + $breadcrumbs->push(Lang::get('lrs.create'), url('/lrs')); +}); +Breadcrumbs::register('editlrs', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('lrs', $lrs); + $breadcrumbs->push(Lang::get('lrs.edit'), url('/lrs/' . $lrs->_id)); +}); +Breadcrumbs::register('lrs.invite', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('lrs.users', $lrs); + $breadcrumbs->push(Lang::get('users.invite.invite'), url('/lrs/' . $lrs->_id . '/users')); +}); + +/* +|--------------------------------------------------------------- +| Site breadcrumbs +|--------------------------------------------------------------- +*/ +Breadcrumbs::register('users', function($breadcrumbs) { + $breadcrumbs->push('Users', url('/site/users')); +}); +Breadcrumbs::register('site.invite', function($breadcrumbs) { + $breadcrumbs->parent('users'); + $breadcrumbs->push('Invite users', url('/site/invite')); +}); +Breadcrumbs::register('settings', function($breadcrumbs) { + $breadcrumbs->push('Settings', url('/site/settings')); +}); +Breadcrumbs::register('site.edit', function($breadcrumbs, $site) { + $breadcrumbs->parent('settings'); + $breadcrumbs->push('Edit settings', url('/site/'. $site->_id . '/edit')); +}); +/* +|--------------------------------------------------------------- +| Statement breadcrumbs +|--------------------------------------------------------------- +*/ +Breadcrumbs::register('statement', function($breadcrumbs, $lrs) { + $breadcrumbs->push('Statements', url('/lrs/'.$lrs->_id.'/statements')); +}); +Breadcrumbs::register('filter', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('statement', $lrs); + $breadcrumbs->push(Lang::get('statements.filter'), url('/lrs/$lrs->_id/statements/generator')); +}); +Breadcrumbs::register('generator', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('statement', $lrs); + $breadcrumbs->push(Lang::get('statements.generator'), url('/lrs/$lrs->_id/statements/generator')); +}); +Breadcrumbs::register('explorer', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('statement', $lrs); + $breadcrumbs->push(Lang::get('statements.explorer'), url('/lrs/$lrs->_id/statements/generator')); +}); +Breadcrumbs::register('analytics', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('statement', $lrs); + $breadcrumbs->push(Lang::get('statements.analytics'), url('/lrs/$lrs->_id/statements/generator')); +}); +Breadcrumbs::register('reporting', function($breadcrumbs, $lrs) { + $breadcrumbs->parent('statement', $lrs); + $breadcrumbs->push(Lang::get('statements.reporting'), url('/lrs/$lrs->_id/statements/generator')); +}); \ No newline at end of file diff --git a/app/commands/.gitkeep b/app/commands/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/config/app.php b/app/config/app.php new file mode 100644 index 0000000000..3b66a65fd1 --- /dev/null +++ b/app/config/app.php @@ -0,0 +1,184 @@ + true, + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | your application so that it is used when running Artisan tasks. + | + */ + + 'url' => 'http://localhost/lrs_new', + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. We have gone + | ahead and set this to a sensible default for you out of the box. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by the translation service provider. You are free to set this value + | to any of the locales which will be supported by the application. + | + */ + + 'locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! + | + */ + + 'key' => '', + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => array( + + 'Illuminate\Foundation\Providers\ArtisanServiceProvider', + 'Illuminate\Auth\AuthServiceProvider', + 'Illuminate\Cache\CacheServiceProvider', + 'Illuminate\Session\CommandsServiceProvider', + 'Illuminate\Foundation\Providers\ConsoleSupportServiceProvider', + 'Illuminate\Routing\ControllerServiceProvider', + 'Illuminate\Cookie\CookieServiceProvider', + 'Illuminate\Database\DatabaseServiceProvider', + 'Illuminate\Encryption\EncryptionServiceProvider', + 'Illuminate\Filesystem\FilesystemServiceProvider', + 'Illuminate\Hashing\HashServiceProvider', + 'Illuminate\Html\HtmlServiceProvider', + 'Illuminate\Log\LogServiceProvider', + 'Illuminate\Mail\MailServiceProvider', + 'Illuminate\Database\MigrationServiceProvider', + 'Illuminate\Pagination\PaginationServiceProvider', + 'Illuminate\Queue\QueueServiceProvider', + 'Illuminate\Redis\RedisServiceProvider', + 'Illuminate\Remote\RemoteServiceProvider', + 'Illuminate\Auth\Reminders\ReminderServiceProvider', + 'Illuminate\Database\SeedServiceProvider', + 'Illuminate\Session\SessionServiceProvider', + 'Illuminate\Translation\TranslationServiceProvider', + 'Illuminate\Validation\ValidationServiceProvider', + 'Illuminate\View\ViewServiceProvider', + 'Illuminate\Workbench\WorkbenchServiceProvider', + 'Locker\Repository\RepositoryServiceProvider', + 'Jenssegers\Mongodb\Auth\ReminderServiceProvider', + 'Jenssegers\Mongodb\MongodbServiceProvider', + 'DaveJamesMiller\Breadcrumbs\ServiceProvider', + 'Barryvdh\Debugbar\ServiceProvider' + + ), + + /* + |-------------------------------------------------------------------------- + | Service Provider Manifest + |-------------------------------------------------------------------------- + | + | The service provider manifest is used by Laravel to lazy load service + | providers which are not needed for each request, as well to keep a + | list of all of the services. Here, you may set its storage spot. + | + */ + + 'manifest' => storage_path().'/meta', + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => array( + + 'App' => 'Illuminate\Support\Facades\App', + 'Artisan' => 'Illuminate\Support\Facades\Artisan', + 'Auth' => 'Illuminate\Support\Facades\Auth', + 'Blade' => 'Illuminate\Support\Facades\Blade', + 'Cache' => 'Illuminate\Support\Facades\Cache', + 'ClassLoader' => 'Illuminate\Support\ClassLoader', + 'Config' => 'Illuminate\Support\Facades\Config', + 'Controller' => 'Illuminate\Routing\Controller', + 'Cookie' => 'Illuminate\Support\Facades\Cookie', + 'Crypt' => 'Illuminate\Support\Facades\Crypt', + 'DB' => 'Illuminate\Support\Facades\DB', + 'Eloquent' => 'Illuminate\Database\Eloquent\Model', + 'Event' => 'Illuminate\Support\Facades\Event', + 'File' => 'Illuminate\Support\Facades\File', + 'Form' => 'Illuminate\Support\Facades\Form', + 'Hash' => 'Illuminate\Support\Facades\Hash', + 'HTML' => 'Illuminate\Support\Facades\HTML', + 'Input' => 'Illuminate\Support\Facades\Input', + 'Lang' => 'Illuminate\Support\Facades\Lang', + 'Log' => 'Illuminate\Support\Facades\Log', + 'Mail' => 'Illuminate\Support\Facades\Mail', + 'Paginator' => 'Illuminate\Support\Facades\Paginator', + 'Password' => 'Illuminate\Support\Facades\Password', + 'Queue' => 'Illuminate\Support\Facades\Queue', + 'Redirect' => 'Illuminate\Support\Facades\Redirect', + 'Redis' => 'Illuminate\Support\Facades\Redis', + 'Request' => 'Illuminate\Support\Facades\Request', + 'Response' => 'Illuminate\Support\Facades\Response', + 'Route' => 'Illuminate\Support\Facades\Route', + 'Schema' => 'Illuminate\Support\Facades\Schema', + 'Seeder' => 'Illuminate\Database\Seeder', + 'Session' => 'Illuminate\Support\Facades\Session', + 'SSH' => 'Illuminate\Support\Facades\SSH', + 'Str' => 'Illuminate\Support\Str', + 'URL' => 'Illuminate\Support\Facades\URL', + 'Validator' => 'Illuminate\Support\Facades\Validator', + 'View' => 'Illuminate\Support\Facades\View', + 'Breadcrumbs' => 'DaveJamesMiller\Breadcrumbs\Facade', + + ), + +); diff --git a/app/config/auth.php b/app/config/auth.php new file mode 100644 index 0000000000..eacbbfaedd --- /dev/null +++ b/app/config/auth.php @@ -0,0 +1,71 @@ + 'eloquent', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | When using the "Eloquent" authentication driver, we need to know which + | Eloquent model should be used to retrieve your users. Of course, it + | is often just the "User" model but you may use whatever you like. + | + */ + + 'model' => 'User', + + /* + |-------------------------------------------------------------------------- + | Authentication Table + |-------------------------------------------------------------------------- + | + | When using the "Database" authentication driver, we need to know which + | table should be used to retrieve your users. We have chosen a basic + | default value but you may easily change it to any table you like. + | + */ + + 'table' => 'users', + + /* + |-------------------------------------------------------------------------- + | Password Reminder Settings + |-------------------------------------------------------------------------- + | + | Here you may set the settings for password reminders, including a view + | that should be used as your password reminder e-mail. You will also + | be able to set the name of the table that holds the reset tokens. + | + | The "expire" time is the number of minutes that the reminder should be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + */ + + 'reminder' => array( + + 'email' => 'emails.auth.reminder', + + 'table' => 'password_reminders', + + 'expire' => 60, + + ), + +); diff --git a/app/config/cache.php b/app/config/cache.php new file mode 100644 index 0000000000..ce89842399 --- /dev/null +++ b/app/config/cache.php @@ -0,0 +1,89 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | File Cache Location + |-------------------------------------------------------------------------- + | + | When using the "file" cache driver, we need a location where the cache + | files may be stored. A sensible default has been specified, but you + | are free to change it to any other place on disk that you desire. + | + */ + + 'path' => storage_path().'/cache', + + /* + |-------------------------------------------------------------------------- + | Database Cache Connection + |-------------------------------------------------------------------------- + | + | When using the "database" cache driver you may specify the connection + | that should be used to store the cached items. When this option is + | null the default database connection will be utilized for cache. + | + */ + + 'connection' => null, + + /* + |-------------------------------------------------------------------------- + | Database Cache Table + |-------------------------------------------------------------------------- + | + | When using the "database" cache driver we need to know the table that + | should be used to store the cached items. A default table name has + | been provided but you're free to change it however you deem fit. + | + */ + + 'table' => 'cache', + + /* + |-------------------------------------------------------------------------- + | Memcached Servers + |-------------------------------------------------------------------------- + | + | Now you may specify an array of your Memcached servers that should be + | used when utilizing the Memcached cache driver. All of the servers + | should contain a value for "host", "port", and "weight" options. + | + */ + + 'memcached' => array( + + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + + ), + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing a RAM based store such as APC or Memcached, there might + | be other applications utilizing the same cache. So, we'll specify a + | value to get prefixed to all our keys so we can avoid collisions. + | + */ + + 'prefix' => 'laravel', + +); diff --git a/app/config/compile.php b/app/config/compile.php new file mode 100644 index 0000000000..54d7185bfb --- /dev/null +++ b/app/config/compile.php @@ -0,0 +1,18 @@ + PDO::FETCH_CLASS, + + /* + |-------------------------------------------------------------------------- + | Default Database Connection Name + |-------------------------------------------------------------------------- + | + | Here you may specify which of the database connections below you wish + | to use as your default connection for all database work. Of course + | you may use many connections at once using the Database library. + | + */ + + 'default' => 'mongodb', + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here are each of the database connections setup for your application. + | Of course, examples of configuring each database platform that is + | supported by Laravel is shown below to make development simple. + | + | + | All database work in Laravel is done through the PHP PDO facilities + | so make sure you have the driver for your particular database of + | choice installed on your machine before you begin development. + | + */ + + 'connections' => array( + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => __DIR__.'/../database/production.sqlite', + 'prefix' => '', + ), + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + ), + + 'pgsql' => array( + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => '', + 'schema' => 'public', + ), + + 'sqlsrv' => array( + 'driver' => 'sqlsrv', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'prefix' => '', + ), + + 'mongodb' => array( + 'driver' => 'mongodb', + 'host' => 'localhost', + 'port' => 27017, + 'username' => '', + 'password' => '', + 'database' => '' + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run in the database. + | + */ + + 'migrations' => 'migrations', + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer set of commands than a typical key-value systems + | such as APC or Memcached. Laravel makes it easy to dig right in. + | + */ + + 'redis' => array( + + 'cluster' => false, + + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0, + ), + + ), + +); diff --git a/app/config/mail.php b/app/config/mail.php new file mode 100644 index 0000000000..6137a5462a --- /dev/null +++ b/app/config/mail.php @@ -0,0 +1,124 @@ + 'smtp', + + /* + |-------------------------------------------------------------------------- + | SMTP Host Address + |-------------------------------------------------------------------------- + | + | Here you may provide the host address of the SMTP server used by your + | applications. A default option is provided that is compatible with + | the Postmark mail service, which will provide reliable delivery. + | + */ + + 'host' => 'smtp.mandrillapp.com', + + /* + |-------------------------------------------------------------------------- + | SMTP Host Port + |-------------------------------------------------------------------------- + | + | This is the SMTP port used by your application to delivery e-mails to + | users of your application. Like the host we have set this value to + | stay compatible with the Postmark e-mail application by default. + | + */ + + 'port' => 2525, + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all e-mails sent by your application to be sent from + | the same address. Here, you may specify a name and address that is + | used globally for all e-mails that are sent by your application. + | + */ + + 'from' => array('address' => 'hello@learninglocker.net', 'name' => 'LearningLocker'), + + /* + |-------------------------------------------------------------------------- + | E-Mail Encryption Protocol + |-------------------------------------------------------------------------- + | + | Here you may specify the encryption protocol that should be used when + | the application send e-mail messages. A sensible default using the + | transport layer security protocol should provide great security. + | + */ + + 'encryption' => 'tls', + + /* + |-------------------------------------------------------------------------- + | SMTP Server Username + |-------------------------------------------------------------------------- + | + | If your SMTP server requires a username for authentication, you should + | set it here. This will get used to authenticate with your server on + | connection. You may also set the "password" value below this one. + | + */ + + 'username' => 'dave.tosh@ht2.co.uk', + + /* + |-------------------------------------------------------------------------- + | SMTP Server Password + |-------------------------------------------------------------------------- + | + | Here you may set the password required by your SMTP server to send out + | messages from your application. This will be given to the server on + | connection so that the application will be able to send messages. + | + */ + + 'password' => 'J6MHP5SkNNQbSe8EOU6-2g', + + /* + |-------------------------------------------------------------------------- + | Sendmail System Path + |-------------------------------------------------------------------------- + | + | When using the "sendmail" driver to send e-mails, we will need to know + | the path to where Sendmail lives on this server. A default path has + | been provided here, which will work well on most of your systems. + | + */ + + 'sendmail' => '/usr/sbin/sendmail -bs', + + /* + |-------------------------------------------------------------------------- + | Mail "Pretend" + |-------------------------------------------------------------------------- + | + | When this option is enabled, e-mail will not actually be sent over the + | web and will instead be written to your application's logs files so + | you may inspect the message. This is great for local development. + | + */ + + 'pretend' => false, + +); \ No newline at end of file diff --git a/app/config/packages/.gitkeep b/app/config/packages/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/config/queue.php b/app/config/queue.php new file mode 100644 index 0000000000..6c0fa52349 --- /dev/null +++ b/app/config/queue.php @@ -0,0 +1,82 @@ + 'sync', + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection information for each server that + | is used by your application. A default configuration has been added + | for each back-end shipped with Laravel. You are free to add more. + | + */ + + 'connections' => array( + + 'sync' => array( + 'driver' => 'sync', + ), + + 'beanstalkd' => array( + 'driver' => 'beanstalkd', + 'host' => 'localhost', + 'queue' => 'default', + ), + + 'sqs' => array( + 'driver' => 'sqs', + 'key' => 'your-public-key', + 'secret' => 'your-secret-key', + 'queue' => 'your-queue-url', + 'region' => 'us-east-1', + ), + + 'iron' => array( + 'driver' => 'iron', + 'project' => 'your-project-id', + 'token' => 'your-token', + 'queue' => 'your-queue-name', + ), + + 'redis' => array( + 'driver' => 'redis', + 'queue' => 'default', + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control which database and table are used to store the jobs that + | have failed. You may change them to any database / table you wish. + | + */ + + 'failed' => array( + + 'database' => 'mysql', 'table' => 'failed_jobs', + + ), + +); diff --git a/app/config/remote.php b/app/config/remote.php new file mode 100644 index 0000000000..ea960e03a5 --- /dev/null +++ b/app/config/remote.php @@ -0,0 +1,59 @@ + 'production', + + /* + |-------------------------------------------------------------------------- + | Remote Server Connections + |-------------------------------------------------------------------------- + | + | These are the servers that will be accessible via the SSH task runner + | facilities of Laravel. This feature radically simplifies executing + | tasks on your servers, such as deploying out these applications. + | + */ + + 'connections' => array( + + 'production' => array( + 'host' => '', + 'username' => '', + 'password' => '', + 'key' => '', + 'keyphrase' => '', + 'root' => '/var/www', + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Remote Server Groups + |-------------------------------------------------------------------------- + | + | Here you may list connections under a single group name, which allows + | you to easily access all of the servers at once using a short name + | that is extremely easy to remember, such as "web" or "database". + | + */ + + 'groups' => array( + + 'web' => array('production') + + ), + +); \ No newline at end of file diff --git a/app/config/session.php b/app/config/session.php new file mode 100644 index 0000000000..ae343029ee --- /dev/null +++ b/app/config/session.php @@ -0,0 +1,140 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to immediately expire on the browser closing, set that option. + | + */ + + 'lifetime' => 120, + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When using the native session driver, we need a location where session + | files may be stored. A default has been set for you but a different + | location may be specified. This is only needed for file sessions. + | + */ + + 'files' => storage_path().'/sessions', + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => null, + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table we + | should use to manage the sessions. Of course, a sensible default is + | provided for you; however, you are free to change this as needed. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => array(2, 100), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the cookie used to identify a session + | instance by ID. The name specified here will get used every time a + | new session cookie is created by the framework for every driver. + | + */ + + 'cookie' => 'laravel_session', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application but you are free to change this when necessary. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | Here you may change the domain of the cookie used to identify a session + | in your application. This will determine which domains the cookie is + | available to in your application. A sensible default has been set. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you if it can not be done securely. + | + */ + + 'secure' => false, + +); diff --git a/app/config/testing/cache.php b/app/config/testing/cache.php new file mode 100644 index 0000000000..16d3ae2fa5 --- /dev/null +++ b/app/config/testing/cache.php @@ -0,0 +1,20 @@ + 'array', + +); \ No newline at end of file diff --git a/app/config/testing/session.php b/app/config/testing/session.php new file mode 100644 index 0000000000..a18c1b9fe5 --- /dev/null +++ b/app/config/testing/session.php @@ -0,0 +1,21 @@ + 'array', + +); \ No newline at end of file diff --git a/app/config/view.php b/app/config/view.php new file mode 100644 index 0000000000..34b8f38735 --- /dev/null +++ b/app/config/view.php @@ -0,0 +1,31 @@ + array(__DIR__.'/../views'), + + /* + |-------------------------------------------------------------------------- + | Pagination View + |-------------------------------------------------------------------------- + | + | This view will be used to render the pagination link output, and can + | be easily customized here to show any view you like. A clean view + | compatible with Twitter's Bootstrap is given to you by default. + | + */ + + 'pagination' => 'pagination::slider-3', + +); diff --git a/app/config/workbench.php b/app/config/workbench.php new file mode 100644 index 0000000000..56bee52658 --- /dev/null +++ b/app/config/workbench.php @@ -0,0 +1,31 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Workbench Author E-Mail Address + |-------------------------------------------------------------------------- + | + | Like the option above, your e-mail address is used when generating new + | workbench packages. The e-mail is placed in your composer.json file + | automatically after the package is created by the workbench tool. + | + */ + + 'email' => '', + +); \ No newline at end of file diff --git a/app/controllers/.gitkeep b/app/controllers/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/controllers/BaseController.php b/app/controllers/BaseController.php new file mode 100644 index 0000000000..097e161a37 --- /dev/null +++ b/app/controllers/BaseController.php @@ -0,0 +1,18 @@ +layout)) + { + $this->layout = View::make($this->layout); + } + } + +} \ No newline at end of file diff --git a/app/controllers/EmailController.php b/app/controllers/EmailController.php new file mode 100644 index 0000000000..22b3b0b807 --- /dev/null +++ b/app/controllers/EmailController.php @@ -0,0 +1,82 @@ +user = $user; + $this->beforeFilter('auth', array('except' => array('index', 'show', 'verifyEmail', 'inviteEmail'))); + + } + + /** + * Verify different email addresses + * + **/ + public function email( $id ){ + + $user = $this->user->find( $id ); + return View::make('partials.users.email')->with('user', $user)->with('email_nav',true); + + } + + /** + * Resend the verification email + **/ + public function resendEmail(){ + + $exists = DB::table('users') + ->where('_id', Auth::user()->_id) + ->where('email', Input::get('email')) + ->pluck('username'); + if( !$exists ){ + return Redirect::back()->with('error', 'There was a problem resending.'); + } + //provide a hook for notification + Event::fire('user.email_resend', array( Auth::user() )); + return Redirect::back()->with('flash', 'Please check your email inbox to verify.'); + } + + /** + * Verify emails + * + **/ + public function verifyEmail( $token ){ + + $message = $this->user->verifyEmail( $token ); + return Redirect::to('/')->with('success', $message); + + } + + /** + * Invite emails + * + **/ + public function inviteEmail( $token ){ + + $message = $this->user->verifyEmail( $token ); + //now login and direct to password reset form + $email = \DB::table('user_tokens') + ->where('token', $token) + ->pluck('email'); + $user = \User::where('email', $email)->first(); + Auth::loginUsingId($user->_id); + return Redirect::to('/users/'. $user->_id . '/add/password'); + + } + + +} \ No newline at end of file diff --git a/app/controllers/LoginController.php b/app/controllers/LoginController.php new file mode 100644 index 0000000000..a56b9198b4 --- /dev/null +++ b/app/controllers/LoginController.php @@ -0,0 +1,40 @@ + Input::get('email'), + 'password' => Input::get('password') + ); + + $remember_me = Input::get('remember', 0); + + if( Auth::attempt($creds, $remember_me) ){ + return Redirect::to('/'); + } + + return Redirect::route('login.create') + ->withInput() + ->withErrors(array('There is a problem with those credentials.')); + + } + + /** + * Logout + **/ + public function destroy(){ + Auth::logout(); + return Redirect::to('/'); + } + +} \ No newline at end of file diff --git a/app/controllers/LrsController.php b/app/controllers/LrsController.php new file mode 100644 index 0000000000..51803dcb22 --- /dev/null +++ b/app/controllers/LrsController.php @@ -0,0 +1,269 @@ +lrs = $lrs; + + $this->beforeFilter('auth'); + $this->beforeFilter('csrf', array('on' => array('store', 'update'))); + $this->beforeFilter('auth.lrs', array('except' => array('index','create','store'))); //check user can access LRS. + $this->beforeFilter('create.lrs', array('only' => 'create')); //Allowed to create an LRS? + + } + + /** + * Display a listing of LRSs available for user. + * + * @return View + */ + public function index(){ + $lrs = $this->lrs->all(); + return View::make('partials.lrs.list', array('lrs' => $lrs)); + } + + /** + * Show the form for creating a new resource. + * + * @return View + */ + public function create(){ + //has the user verified their email address? + $verified = Auth::user()->verified; + return View::make('partials.lrs.create', array('verified' => $verified)); + } + + /** + * Store a newly created resource in storage. + * + * @return View + */ + public function store(){ + + // Store lrs + $s = $this->lrs->create( Input::all() ); + + if($s){ + return Redirect::to('/lrs')->with('success', Lang::get('lrs.created')); + } + + return Redirect::back() + ->withInput() + ->with('error', Lang::get('create_problem')); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return View + */ + public function edit( $id ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.lrs.edit', array('account_nav' => true, + 'lrs' => $lrs, + 'list' => $lrs_list)); + + } + + /** + * Update the specified resource in storage. + * + * @param int $id + * @return View + */ + public function update($id){ + + $l = $this->lrs->update( $id, Input::all() ); + + if($l){ + return Redirect::back()->with('success', Lang::get('lrs.updated')); + } + + return Redirect::back() + ->withInput() + ->withErrors($this->lrs->errors()); + + } + + /** + * Display the specified resource. + * + * @param int $id + * @return View + */ + public function show( $id ){ + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + $stats = new \app\locker\data\LrsDashboard( $id ); + return View::make('partials.lrs.dashboard', array('stats' => $stats->stats, + 'lrs' => $lrs, + 'list' => $lrs_list, + 'dash_nav' => true)); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return View + */ + public function destroy($id){ + + $this->lrs->delete($id); + return Redirect::back()->with('success', Lang::get('lrs.deleted')); + + } + + /** + * Display statements for this LRS + * + * @return View + */ + public function statements( $id ){ + + $statements = $this->lrs->statements( $id ); + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.statements.list', + array('statements' => $statements, + 'lrs' => $lrs, + 'list' => $lrs_list, + 'statement_nav' => true)); + + } + + /** + * Display the analytics view. + * + * @return View + */ + public function analytics( $id, $segment='verbCloud' ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + $data = new \app\locker\data\Analytics( $id, $segment, Input::all() ); + return View::make('partials.lrs.analytics', array('lrs' => $lrs, + 'data' => $data->results, + 'analytics_nav' => true, + 'list' => $lrs_list)); + + } + + /** + * Display the reporting view. + * + * @return View + */ + public function reporting( $id ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + //$data = new \app\locker\data\Reporting( $id, Input::all() ); + return View::make('partials.lrs.reporting', array('lrs' => $lrs, + 'reporting_nav' => true, + 'list' => $lrs_list)); + + } + + /** + * Display the endpoint view. + * + * @return View + */ + public function endpoint( $id ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.lrs.endpoint', array('lrs' => $lrs, + 'endpoint_nav' => true, + 'list' => $lrs_list)); + + } + + /** + * Display the api view. + * + * @return View + */ + public function api( $id ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.lrs.api', array('lrs' => $lrs, + 'api_nav' => true, + 'list' => $lrs_list)); + + } + + /** + * Generate a new key and secret for basic auth + * + **/ + public function editCredentials( $id ){ + + $lrs = $this->lrs->find( $id ); + + $lrs->api = array('basic_key' => \app\locker\helpers\Helpers::getRandomValue(), + 'basic_secret' => \app\locker\helpers\Helpers::getRandomValue()); + + if( $lrs->save() ){ + $message_type = 'success'; + $message = Lang::get('update_key'); + }else{ + $message_type = 'error'; + $message = Lang::get('update_key_error'); + } + + return Redirect::back()->with($message_type, $message); + + } + + /** + * Display users with access to this lrs. + * + * @return View + */ + public function users( $id ){ + + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.users.list', array('lrs' => $lrs, + 'users' => $lrs->users, + 'list' => $lrs_list, + 'user_nav' => true)); + + } + + public function inviteUsersForm( $id ){ + $lrs = $this->lrs->find( $id ); + $lrs_list = $this->lrs->all(); + return View::make('partials.lrs.invite', array('lrs' => $lrs, + 'users' => $lrs->users, + 'list' => $lrs_list, + 'user_nav' => true)); + + } + + public function usersRemove( $id ){ + $lrs = $this->lrs->removeUser( $id, Input::get('user') ); + return Redirect::back()->with('success', Lang::get('lrs.remove_user')); + } + +} \ No newline at end of file diff --git a/app/controllers/PasswordController.php b/app/controllers/PasswordController.php new file mode 100644 index 0000000000..541dbddeb5 --- /dev/null +++ b/app/controllers/PasswordController.php @@ -0,0 +1,133 @@ +user = $user; + + } + + public function remind(){ + return View::make('system.password.remind'); + } + + public function request(){ + + $response = Password::remind(Input::only('email'), function($message, $user){ + $message->subject(Lang::get('users.reset')); + }); + + if( $response == Password::INVALID_USER ) + return Redirect::back()->with('error', Lang::get($response)); + else + return Redirect::back()->with('success', Lang::get($response)); + + } + + public function reset( $token = null ){ + + if (is_null($token)) App::abort(404); + + return View::make('system.password.reset')->with('token', $token); + + } + + /** + * Handle the POST request to reset password + * + * @return response + * + **/ + public function postReset(){ + + $credentials = Input::only( + 'email', 'password', 'password_confirmation', 'token' + ); + + $response = Password::reset($credentials, function($user, $password){ + $user->password = Hash::make($password); + $user->save(); + }); + + switch ( $response ){ + case Password::INVALID_PASSWORD: + case Password::INVALID_TOKEN: + case Password::INVALID_USER: + return Redirect::back()->with('error', Lang::get($response)); + + case Password::PASSWORD_RESET: + return Redirect::to('/'); + } + + } + + /** + * Update the user's password. + * + * @param int $id + * @return View + */ + public function updatePassword( $id ){ + + //check existing password + $pass_check = Hash::check(Input::get('current_password'), Auth::user()->password); + if( !$pass_check ) return Redirect::back()->withErrors( array( lang::get('password_current_wrong') ) ); + + $rules['password'] = 'required|confirmed'; + $validator = Validator::make(Input::all(), $rules); + if ($validator->fails()) return Redirect::back()->withErrors($validator); + + // Update the user + $s = $this->user->updatePassword($id, Input::get('password')); + + if($s){ + return Redirect::back()->with('success', Lang::get('users.success')); + } + + return Redirect::back() + ->withInput() + ->withErrors( array( Lang::get('users.password_problem') ) ); + + } + + /** + * Add a password - this is used when users are invited into the platform. + * + **/ + public function addPasswordForm(){ + return View::make('partials.users.addPassword', array( 'user' => Auth::user() )); + } + + /** + * Add a password, the action - this is used when users are invited into the platform + * + **/ + public function addPassword( $id ){ + + $rules['password'] = 'required|confirmed'; + $validator = Validator::make(Input::all(), $rules); + if ($validator->fails()) return Redirect::back()->withErrors($validator); + + // Update the user + $user = \User::find($id); + $user->password = Input::get('password'); + + if( $user->save() ){ + return Redirect::to('/')->with('success', Lang::get('users.success')); + } + + return Redirect::back() + ->withInput() + ->withErrors( array( Lang::get('users.password_problem') ) ); + + } + +} \ No newline at end of file diff --git a/app/controllers/RegisterController.php b/app/controllers/RegisterController.php new file mode 100644 index 0000000000..b4d46c787e --- /dev/null +++ b/app/controllers/RegisterController.php @@ -0,0 +1,64 @@ +user = $user; + $this->beforeFilter('guest'); + $this->beforeFilter('registation.status'); + + } + + + public function index(){ + return View::make('system.forms.register'); + } + + public function store(){ + + //event hook to fire and check domain registration + $event = Event::fire('user.domain_check', array(Input::all())); + if( $event == false ){ + return Redirect::back()->withErrors('That email address is not premitted.'); + } + + //validate input + $validator = $this->user->validate( Input::all() ); + if ($validator->fails()){ + return Redirect::back() + ->withInput(Input::except('password')) + ->withErrors($validator); + } + + // Save the user + $user = $this->user->create(Input::all()); + + if($user){ + //event hook to fire upon successful regitration + Event::fire('user.register', array($user)); + // log in new user + Auth::attempt(array( + 'email' => Input::get('email'), + 'password' => Input::get('password') + )); + return Redirect::to('/') + ->with('flash', 'The new user has been created'); + } + + return Redirect::to('/'); + } + +} \ No newline at end of file diff --git a/app/controllers/SiteController.php b/app/controllers/SiteController.php new file mode 100644 index 0000000000..4e78488723 --- /dev/null +++ b/app/controllers/SiteController.php @@ -0,0 +1,159 @@ +site = $site; + + $this->beforeFilter('auth'); + $this->beforeFilter('csrf', array('on' => 'update', 'verifyUser')); + $this->beforeFilter('auth.super'); + + } + + /** + * Display a listing of statements for a user. + * + * @return View + */ + public function index(){ + + $stats = new \app\locker\data\AdminDashboard(); + $site = $this->site->all(); + return View::make('partials.site.dashboard', + array('stats' => $stats->stats, + 'site' => $site, + 'dash_nav' => true, + 'admin_dash' => true)); + + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return View + */ + public function edit( $id ){ + + $site = $this->site->find( $id ); + return View::make('partials.site.edit', array('site' => $site, + 'settings_nav' => true)); + + } + + /** + * Update the specified resource in storage. + * + * @param int $id + * @return View + */ + public function update($id){ + + // Update site details + $s = $this->site->update( $id, Input::all() ); + + if($s){ + return Redirect::back()->with('success', Lang::get('site.updated')); + } + + return Redirect::back() + ->withInput() + ->withErrors($user->errors()); + + } + + /** + * Display the super admin settings. + * + * @return Response + */ + public function settings(){ + + $site = $this->site->all(); + return View::make('partials.site.settings', array('site' => $site, + 'settings_nav' => true, + 'admin_dash' => true)); + + } + + /** + * Display the super admin lrs view. + * + * @return Response + */ + public function lrs(){ + + $lrs = Lrs::all(); + if( $lrs ){ + foreach( $lrs as $l ){ + $l->user_total = 0; + $l->statement_total = 0; + } + } + return View::make('partials.lrs.list', array('lrs' => $lrs, + 'lrs_nav' => true, + 'admin_dash' => true)); + + } + + /** + * Display the super admin user list view. + * + * @return Response + */ + public function users(){ + + $users = User::orderBy('created_at', 'asc')->get(); + foreach($users as &$u){ + $u->lrs_owned = Lrs::where('owner._id', $u->_id)->select('title')->get()->toArray(); + $u->lrs_member = Lrs::where('users.user', $u->_id)->select('title')->get()->toArray(); + } + return View::make('partials.users.list', array('users' => $users, 'users_nav' => true, 'admin_dash' => true)); + + } + + /** + * Display the invite user page + * + * @return Response + */ + public function inviteUsersForm(){ + return View::make('partials.site.invite', array('users_nav' => true, + 'admin_dash' => true)); + } + + /** + * Invite in the users + * + **/ + public function inviteUsers(){ + $this->site->inviteusers( Input::all() ); + return Redirect::back() + ->with('success', Lang::get('users.invite.invited')); + } + + /** + * Verify a user. + **/ + public function verifyUser($id){ + $verify = $this->site->verifyUser($id); + return Redirect::back() + ->with('success', Lang::get('users.verify_success')); + } + +} \ No newline at end of file diff --git a/app/controllers/StatementController.php b/app/controllers/StatementController.php new file mode 100644 index 0000000000..fe24c82fbc --- /dev/null +++ b/app/controllers/StatementController.php @@ -0,0 +1,165 @@ +statement = $statement; + + $this->beforeFilter('auth', array('except')); + $this->beforeFilter('csrf', array('on' => 'post')); + $this->beforeFilter('@checkCanSubmit', array('only' => 'store')); + + } + + /** + * Display a listing of statements for a user. + * + * @return Response + */ + public function index(){} + + /** + * Show the form for creating a new resource. + * + * @return View + */ + public function create( $id ){ + $lrs = Lrs::find( $id ); + return View::make('partials.statements.create', array('lrs' => $lrs, + 'statement_nav' => true)); + } + + /** + * Store a newly created resource in storage. + * + * @return Response + */ + public function store(){ + + $input = Input::all(); + + //get lrs + $lrs = Lrs::where( '_id', $input['lrs'] ) + ->select('_id', 'title') + ->first(); + + //remove lrs and _token from Input + unset( $input['lrs'] ); + unset( $input['_token'] ); + + //add mailto to actor mbox + $input['actor']['mbox'] = 'mailto:' . $input['actor']['mbox']; + + // Save the statement + $s = $this->statement->create( $input, $lrs ); + + if($s){ + return Redirect::back()->with('success', Lang::get('statement.added')); + } + + return Redirect::back() + ->withInput() + ->withErrors($s->errors()); + + } + + /** + * Display the specified resource. + * + * @param int $id + * @return Response + */ + public function show( $id ){} + + public function explore( $id ){ + + $vars = explode( '/', Request::path() ); + + //remove lrs and statement which is always passed + unset( $vars[0] ); //remove lrs + unset( $vars[1] ); //remove lrs id + unset( $vars[2] ); //remove statements + unset( $vars[3] ); //remove explorer + + $lrs = Lrs::find( $id ); + $lrs_list = \Lrs::all(); + $statements = $this->statement->filter( $id, $vars, 'comments' ); + $graph_it = new \app\locker\data\Filter( $statements['data'] ); + + return View::make('partials.statements.explore', array('lrs' => $lrs, + 'list' => $lrs_list, + 'statements' => $statements['statements'], + 'total' => count( $statements['data'] ), + 'filter' => $statements['filter'], + 'single_bar_data' => $graph_it->timeline_data, + 'statement_nav' => true)); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return Response + */ + public function filter( $id, $extra ){ + + $vars = explode( '/', Request::path() ); + + //remove lrs and statement which is always passed + unset( $vars[0] ); + unset( $vars[1] ); + unset( $vars[2] ); + + $statements = $this->statement->filter( $id, $vars ); + $graph_it = new \app\locker\data\Filter( $statements['data'] ); + $lrs = \Lrs::find( $id ); + $lrs_list = \Lrs::all(); + + return View::make('partials.statements.filter', + array('statements' => $statements['statements'], + 'lrs' => $lrs, + 'single_bar_data' => $graph_it->timeline_data, + 'total' => count( $statements['data'] ), + 'list' => $lrs_list, + 'filter' => $statements['filter'], + 'statement_nav' => true)); + + } + + /** + * Can current user submit statements to this LRS? + **/ + public function checkCanSubmit( $route, $request ){ + + $user = \Auth::user(); + $lrs = Lrs::find( Input::get('lrs') ); + $get_users = array(); + + if( $lrs ){ + foreach( $lrs->users as $u ){ + $get_users[] = $u['_id']; + } + } + + //check current user is in the list of allowed users, or is super admin + if( !in_array($user->_id, $get_users) && $user->role != 'super' ){ + return Redirect::to('/'); + } + + } + +} \ No newline at end of file diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php new file mode 100644 index 0000000000..f9bb6c1f8b --- /dev/null +++ b/app/controllers/UserController.php @@ -0,0 +1,120 @@ +user = $user; + $this->logged_in_user = Auth::user(); + + $this->beforeFilter('auth', array('except' => array('verifyEmail','addPasswordForm'))); + $this->beforeFilter('csrf', array('on' => array('update','updatePassword', 'addPassword', 'destroy'))); + $this->beforeFilter('user.delete', array('only' => 'destroy')); + $this->beforeFilter('auth.super', array('only' => 'updateRole')); + + } + + /** + * Display a listing of users. + * + * @return View + */ + public function index(){ + + return View::make('index', array( 'users' => $this->user->all() )); + + } + + /** + * Show the form for creating a new resource. + * + * @return View + */ + public function create(){ + + return View::make('register.index'); + + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return View + */ + public function edit( $id ){ + + return View::make('partials.users.edit') + ->with( 'user', $this->user->find( $id ) ); + + } + + /** + * Update the specified resource in storage. + * + * @param int $id + * @return View + */ + public function update( $id ){ + + $rules['email'] = 'required|email|unique:users'; + $rules['name'] = 'required'; + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) return Redirect::back()->withErrors($validator); + + // Update the user + $s = $this->user->update($id, Input::all()); + + if($s){ + return Redirect::back()->with('success', Lang::get('users.updated')); + } + + return Redirect::back() + ->withInput() + ->with('error', Lang::get('users.updated_error')); + + } + + /** + * Update the user's role. + * + * @param int $id + * @return View + */ + public function updateRole( $id ){ + $s = $this->user->updateRole($id, Input::get('role')); + return Redirect::to('/site/users') + ->with('success', Lang::get('users.role_change')); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return View + */ + public function destroy( $id ){ + //@todo transfer ownership of all LRSs they admin to super admin + + //delete + $this->user->delete( $id ); + return Redirect::back()->with('success', Lang::get('users.deleted')); + } + + +} \ No newline at end of file diff --git a/app/controllers/api/BaseController.php b/app/controllers/api/BaseController.php new file mode 100644 index 0000000000..61b0860715 --- /dev/null +++ b/app/controllers/api/BaseController.php @@ -0,0 +1,65 @@ + \Config::get('api.using_version'), + 'route' => \Request::path() + ); + + + $json['url_params'] = \Route::getCurrentRoute()->getParameters(); + + $params = \Input::all(); + + if( sizeof($additional_params) > 0 ){ + $params = array_merge( $params, $additional_params); + } + $json['params'] = $params; + + + if( sizeof($extra)>0){ + $json = array_merge( $json, $extra ); + } + + $json['results'] = $results; + + if( \Config::get('app.debug') ){ + $json['debug'] = array( + 'sql' => \DB::getQueryLog() + ); + + $json['debug'] = array_merge($json['debug'], $debug ); + } + + + return \Response::json( $json ); + // + return json_encode($json); + } + + /** + * GENERIC MODEL HANDLING + **/ + + protected function findModel( $modelType, $id, $with=array() ){ + $model = $modelType::with( $with )->find($id); + + if( is_null($model)){ + \App::abort(404, 'Model not found'); + + } else { + return $model; + } + } + + protected function returnModel($model){ + return $this->returnJSON( $model->toArray() ); + } + +} \ No newline at end of file diff --git a/app/controllers/api/StatementController.php b/app/controllers/api/StatementController.php new file mode 100644 index 0000000000..4355702716 --- /dev/null +++ b/app/controllers/api/StatementController.php @@ -0,0 +1,81 @@ +statement = $statement; + $this->beforeFilter('@checkVersion', array('only' => 'store')); + + } + + /** + * Store a newly created resource in storage. + * + * @return Response + */ + public function store(){ + + //grab incoming statement + $request = \Request::instance(); + $incoming_statement = $request->getContent(); + + //convert to array + $statement = json_decode($incoming_statement, TRUE); + + //get the lrs + $key = \Request::getUser(); + $secret = \Request::getPassword(); + $lrs = \Lrs::where('api.basic_key', $key) + ->where('api.basic_secret', $secret) + ->first(); + + // Save the statement + $save = $this->statement->create( $statement, $lrs ); + + if( $save['success'] ){ + return \Response::json( array( 'success' => true, + 'id' => $save['id'] ), + 200 ); + }else{ + return \Response::json( array( 'error' => true, + 'message' => implode($save['message'])), + 400); + } + + } + + public function update(){} + + public function index(){} + + /** + * Check request header for correct xAPI version + **/ + public function checkVersion( $route, $request ){ + + //should be X-Experience-API-Version: 1.0.0 or 1.0.1 (can accept 1.0), reject everything else. + $version = \Request::header('X-Experience-API-Version'); + + if( !isset($version) || ( $version < '1.0.0' || $version > '1.0.9' ) && $version != '1.0' ){ + return \Response::json( array( 'error' => true, + 'message' => 'This statement is not the correct version of xAPI.'), + 400); + } + + } + +} \ No newline at end of file diff --git a/app/database/migrations/.gitkeep b/app/database/migrations/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/database/production.sqlite b/app/database/production.sqlite new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/database/seeds/.gitkeep b/app/database/seeds/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/database/seeds/DatabaseSeeder.php b/app/database/seeds/DatabaseSeeder.php new file mode 100644 index 0000000000..6a8c204c9c --- /dev/null +++ b/app/database/seeds/DatabaseSeeder.php @@ -0,0 +1,17 @@ +call('UserTableSeeder'); + } + +} \ No newline at end of file diff --git a/app/filters.php b/app/filters.php new file mode 100644 index 0000000000..f22feb4f14 --- /dev/null +++ b/app/filters.php @@ -0,0 +1,249 @@ +headers->set('X-Experience-API-Version', '1.0.1'); +}); + +/* +|-------------------------------------------------------------------------- +| Authentication Filters +|-------------------------------------------------------------------------- +| +| The following filters are used to verify that the user of the current +| session is logged into this application. The "basic" filter easily +| integrates HTTP Basic authentication for quick, simple checking. +| +*/ + +Route::filter('auth', function() +{ + if (Auth::guest()) return Redirect::guest('/'); +}); + +Route::filter('auth.basic', function() +{ + return Auth::basic(); +}); + + +/* +|-------------------------------------------------------------------------- +| Submit statement via basic http authentication +|-------------------------------------------------------------------------- +| +| Login in once using key / secret to allow saving a statement. +| +*/ + +Route::filter('auth.statement', function(){ + + //set passed credentials + $key = Request::getUser(); + $secret = Request::getPassword(); + + //see if the lrs exists based on key and secret + $lrs = \Lrs::where('api.basic_key', $key) + ->where('api.basic_secret', $secret) + ->select('owner._id')->first(); + + //if no id found, return error + if ( $lrs == NULL ) { + return Response::json(array( + 'error' => true, + 'message' => 'Unauthorized request.'), + 401 + ); + } + + //attempt login once + if ( ! Auth::onceUsingId($lrs->owner['_id']) ) { + return Response::json(array( + 'error' => true, + 'message' => 'Unauthorized Request'), + 401 + ); + } + +}); + + +/* +|-------------------------------------------------------------------------- +| Check for super admin +|-------------------------------------------------------------------------- +| +| Check the logged in user is a super admin. +| +*/ + +Route::filter('auth.super', function( $route, $request ){ + if( Auth::user()->role != 'super' ){ + return Redirect::to('/'); + } +}); + +/* +|-------------------------------------------------------------------------- +| LRS admin access +|-------------------------------------------------------------------------- +| +| Check the logged in user has admin privilages for current LRS. If not, +| then redirect to home page without a message. +| +*/ + +Route::filter('auth.admin', function( $route, $request ){ + + $lrs = Lrs::find( $route->parameter('lrs') ); + $user = Auth::user()->_id; + $is_admin = false; + foreach( $lrs->users as $u ){ + //is the user on the LRS user list with role admin? + if($u['user'] == $user && $u['role'] == 'admin'){ + $is_admin = true; + } + } + if( !$is_admin ){ + return Redirect::to('/'); + } + +}); + +/* +|-------------------------------------------------------------------------- +| LRS access +|-------------------------------------------------------------------------- +| +| Check logged in user can access the current lrs. To access an LRS you have +| to be the site super admin, the LRS owner (admin) or have been invited in. +| +*/ + +Route::filter('auth.lrs', function( $route, $request ){ + //check to see if lrs id exists? + $lrs = Lrs::find( $route->parameter('id') ); + //if not, let's try the lrs parameter + if( !$lrs ){ + $lrs = Lrs::find( $route->parameter('lrs') ); + } + $user = \Auth::user(); + + if( $lrs ){ + //get all users will access to the lrs + foreach( $lrs->users as $u ){ + $get_users[] = $u['_id']; + } + //check current user is in the list of allowed users, or is super admin + if( !in_array($user->_id, $get_users) && $user->role != 'super' ){ + return Redirect::to('/'); + } + + }else{ + return Redirect::to('/'); + } + +}); + +/* +|-------------------------------------------------------------------------- +| Who can create a new LRS? +|-------------------------------------------------------------------------- +| +| Super admins can decide who is allowed to create new LRSs. Super, existing +| admins or everyone, including observers. +| +*/ + +Route::filter('create.lrs', function( $route, $request ){ + + $site = Site::first(); + $allowed = $site->create_lrs; + $user_role = \Auth::user()->role; + + if( !in_array($user_role, $allowed) ){ + return Redirect::to('/'); + } + +}); + +/* +|--------------------------------------------------------------------------- +| Check whether registration has been enabled +|--------------------------------------------------------------------------- +*/ + +Route::filter('registation.status', function( $route, $request ){ + $site = \Site::first(); + if( $site ){ + if( $site->registration != 'Open' ) return Redirect::to('/'); + } +}); + +/* +|--------------------------------------------------------------------------- +| Check the person deleting a user account, is allowed to. +| +| User's can delete their own account as can super admins +|--------------------------------------------------------------------------- +*/ + +Route::filter('user.delete', function( $route, $request ){ + $user = $route->parameter('users'); + if( \Auth::user()->_id != $user && !\app\locker\helpers\Access::isRole('super') ){ + return Redirect::to('/'); + } +}); + + +/* +|-------------------------------------------------------------------------- +| Guest Filter +|-------------------------------------------------------------------------- +| +| The "guest" filter is the counterpart of the authentication filters as +| it simply checks that the current user is not logged in. A redirect +| response will be issued if they are, which you may freely change. +| +*/ + +Route::filter('guest', function() +{ + if (Auth::check()) return Redirect::to('/'); +}); + +/* +|-------------------------------------------------------------------------- +| CSRF Protection Filter +|-------------------------------------------------------------------------- +| +| The CSRF filter is responsible for protecting your application against +| cross-site request forgery attacks. If this special token in a user +| session does not match the one given in this request, we'll bail. +| +*/ + +Route::filter('csrf', function() +{ + if (Session::token() != Input::get('_token')) + { + throw new Illuminate\Session\TokenMismatchException; + } +}); \ No newline at end of file diff --git a/app/lang/en/lrs.php b/app/lang/en/lrs.php new file mode 100644 index 0000000000..8a1fa3371c --- /dev/null +++ b/app/lang/en/lrs.php @@ -0,0 +1,52 @@ + 'Add LRS', + 'home' => 'LRS home', + 'create' => 'Create an LRS', + 'delete_confirm' => 'Are you sure you want to delete this LRS? THERE IS NO UNDO AND YOU WILL LOSE ALL STATEMENTS.', + 'edit' => 'Edit an LRS', + 'new' => 'Create new LRS', + 'verify' => 'You need to verify your email before you can create an LRS.', + 'list' => 'LRS List', + 'none' => 'No LRS\'s available.', + 'reporting' => 'Reporting', + 'deleted' => 'The LRS was deleted', + 'updated' => 'The LRS was updated', + 'create_problem' => 'There was a problem creating that LRS.', + 'created' => 'The LRS was created.', + + 'sidebar' => array( + 'dash' => 'Dashboard', + 'edit' => 'Edit LRS details', + 'endpoint' => 'xAPI statements', + 'api' => 'LRS API', + 'users' => 'Manage users', + 'analytics' => 'Analytics', + 'reporting' => 'Reporting' + ), + + 'endpoint' => array( + 'endpoint' => 'Endpoint', + 'basic_http' => 'Basic HTTP Authentication', + 'submit' => 'Accept xAPI Statements', + 'instructions' => 'In order to accept xAPI statements you will need to submit to the following endpoint and authenticate.', + 'new_key_secret' => 'Generate new key and secret' + ), + + 'api' => array( + 'oauth' => 'OAuth 2.0', + 'settings' => 'API Settings', + 'api' => 'LRS API' + ), + + 'update_key' => 'Your key has now been updated.', + 'update_key_error' => 'There was an error updating your key and secret.', + 'remove_user' => 'That user has been removed from this LRS.' +); \ No newline at end of file diff --git a/app/lang/en/pagination.php b/app/lang/en/pagination.php new file mode 100644 index 0000000000..eb9be3baae --- /dev/null +++ b/app/lang/en/pagination.php @@ -0,0 +1,20 @@ + '« Previous', + + 'next' => 'Next »', + +); \ No newline at end of file diff --git a/app/lang/en/reminders.php b/app/lang/en/reminders.php new file mode 100644 index 0000000000..e42148e9fb --- /dev/null +++ b/app/lang/en/reminders.php @@ -0,0 +1,24 @@ + "Passwords must be at least six characters and match the confirmation.", + + "user" => "We can't find a user with that e-mail address.", + + "token" => "This password reset token is invalid.", + + "sent" => "Password reminder sent!", + +); diff --git a/app/lang/en/site.php b/app/lang/en/site.php new file mode 100644 index 0000000000..da77ee3a20 --- /dev/null +++ b/app/lang/en/site.php @@ -0,0 +1,66 @@ + 'edit', + 'delete' => 'delete', + 'username' => 'Username', + 'password' => 'Password', + 'title' => 'Title', + 'name' => 'Name', + 'description' => 'Description', + 'logo' => 'Logo', + 'language' => 'Language', + 'submit' => 'Submit', + 'remove' => 'Remove', + 'settings' => 'Settings', + 'dash' => 'Dashboard', + 'admin_dash' => 'Admin dashboard', + 'logout' => 'Logout', + 'email' => 'Site email', + 'account' => 'Account settings', + 'yes' => 'Yes', + 'no' => 'No', + 'on' => 'On', + 'off' => 'Off', + 'super_admin' => 'Super admin', + 'admin' => 'Admin', + 'observers' => 'Observers', + 'open' => 'Open', + 'closed' => 'Closed', + 'details' => 'Details', + + 'total_learners' => 'Total learners', + 'statement_total' => 'Statement total', + 'learner_number' => 'Learner number', + 'activity_sources' => 'Activity sources', + 'api_calls' => 'API calls', + + 'create_lrs' => 'Who can create new LRSs?', + 'api_status' => 'API Status', + 'registration' => 'Registration', + 'restrict' => 'Restrict registration', + 'claim' => 'Learners claim?', + + 'help' => array( + 'email' => 'The main contact email for this instance of Learning Locker', + 'create_lrs' => 'Select which user roles can create new LRSs', + 'api_status' => 'Turn on the Learning Locker API. @todo', + 'registration' => 'Is user registration for the site open or closed?', + 'restrict' => 'Restrict registration to a particular email domain. Just enter the domain part e.g. ht2.co.uk', + 'claim' => 'Can learners\' login and claim all xAPI statements that belong to them?', + 'language' => 'Default language to be used when generating statements using the statement generator' + ), + + 'updated' => 'Learning Locker details have been updated', +); \ No newline at end of file diff --git a/app/lang/en/statements.php b/app/lang/en/statements.php new file mode 100644 index 0000000000..c7da55747c --- /dev/null +++ b/app/lang/en/statements.php @@ -0,0 +1,17 @@ + 'Statements', + 'generator' => 'Generator', + 'filter' => 'filter', + 'explorer' => 'Explorer', + 'reporting' => 'Reporting', + 'analytics' => 'Analytics', + 'added' => 'Your statement has been added.' +); \ No newline at end of file diff --git a/app/lang/en/users.php b/app/lang/en/users.php new file mode 100644 index 0000000000..862562089d --- /dev/null +++ b/app/lang/en/users.php @@ -0,0 +1,41 @@ + 'Users', + 'role' => 'Role', + 'invite' => array( + 'invite' => 'Invite users', + 'email' => 'Email addresses (separate lines)', + 'message' => 'Message (optional)', + 'sample' => 'I would like to invite you to join this LRS.', + 'invited' => 'Those users have been invited.' + ), + 'password' => 'Password', + 'password_again' => 'Password confirm', + 'password_current' => 'Current password', + 'password_change' => 'Change password', + 'password_add' => 'Add a password', + 'password_problem' => 'There was a problem saving your password.', + 'password_current_wrong' => 'Your current password was not correct.', + 'password_instructions' => 'Please add a password for your account. You need to do this before you can continue.', + 'email' => 'Email', + 'verfiy_success' => 'You have verified this user.', + 'verified' => 'Verified', + 'verify_request' => 'Please check your email for next steps.', + 'email_verified' => 'Thanks, you have now validated your email.', + 'email_verify_problem' => 'There was a problem validating your email address.', + 'unverified' => 'Unverified', + 'verify_resend' => 'Resend verify email', + 'reset' => 'Reset your password', + 'success' => 'Your password has been saved', + 'role_change' => 'The user\'s role has been changed.', + 'deleted' => 'The user was delete and any LRSs they created transferred to the site admin.', + 'updated' => 'Account settings have been updated', + 'updated_error' => 'There was a problem updating that account.', +); \ No newline at end of file diff --git a/app/lang/en/validation.php b/app/lang/en/validation.php new file mode 100644 index 0000000000..94823021c1 --- /dev/null +++ b/app/lang/en/validation.php @@ -0,0 +1,98 @@ + "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "after" => "The :attribute must be a date after :date.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "array" => "The :attribute must be an array.", + "before" => "The :attribute must be a date before :date.", + "between" => array( + "numeric" => "The :attribute must be between :min and :max.", + "file" => "The :attribute must be between :min and :max kilobytes.", + "string" => "The :attribute must be between :min and :max characters.", + "array" => "The :attribute must have between :min and :max items.", + ), + "confirmed" => "The :attribute confirmation does not match.", + "date" => "The :attribute is not a valid date.", + "date_format" => "The :attribute does not match the format :format.", + "different" => "The :attribute and :other must be different.", + "digits" => "The :attribute must be :digits digits.", + "digits_between" => "The :attribute must be between :min and :max digits.", + "email" => "The :attribute format is invalid.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => array( + "numeric" => "The :attribute may not be greater than :max.", + "file" => "The :attribute may not be greater than :max kilobytes.", + "string" => "The :attribute may not be greater than :max characters.", + "array" => "The :attribute may not have more than :max items.", + ), + "mimes" => "The :attribute must be a file of type: :values.", + "min" => array( + "numeric" => "The :attribute must be at least :min.", + "file" => "The :attribute must be at least :min kilobytes.", + "string" => "The :attribute must be at least :min characters.", + "array" => "The :attribute must have at least :min items.", + ), + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "regex" => "The :attribute format is invalid.", + "required" => "The :attribute field is required.", + "required_if" => "The :attribute field is required when :other is :value.", + "required_with" => "The :attribute field is required when :values is present.", + "required_without" => "The :attribute field is required when :values is not present.", + "same" => "The :attribute and :other must match.", + "size" => array( + "numeric" => "The :attribute must be :size.", + "file" => "The :attribute must be :size kilobytes.", + "string" => "The :attribute must be :size characters.", + "array" => "The :attribute must contain :size items.", + ), + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array(), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); diff --git a/app/listeners.php b/app/listeners.php new file mode 100644 index 0000000000..6b42096965 --- /dev/null +++ b/app/listeners.php @@ -0,0 +1,20 @@ +setDb(); + + $this->user = \Auth::user(); //we might want to pass user in, for example when use the API + + $this->setFullStats(); + + } + + /** + * Set all stats array. + * + **/ + public function setFullStats(){ + $this->stats = array('statement_count' => $this->statementCount(), + 'lrs_count' => $this->lrsCount(), + 'actor_count' => $this->actorCount(), + 'user_count' => $this->userCount(), + 'statement_graph' => $this->getStatementNumbersByDate(), + 'statement_avg' => $this->statementAvgCount(), + 'learner_avg' => $this->learnerAvgCount() + ); + } + + /** + * Count all statements in Learning Locker + * + * @return count + * + **/ + public function statementCount(){ + return \DB::collection('statements')->remember(5)->count(); + } + + /** + * Count all LRSs in Learning Locker + * + * @return count + * + **/ + public function lrsCount(){ + return \DB::collection('lrs')->remember(5)->count(); + } + + /** + * Count the number of distinct actors within Learning Locker statements. + * + * @return count. + * + **/ + public function actorCount(){ + return count( \Statement::distinct('actor.mbox')->remember(5)->get() ); + } + + /** + * Count the number of users in Learning Locker. + * + * @return count. + * + **/ + public function userCount(){ + return \DB::collection('users')->remember(5)->count(); + } + + /** + * Get a count of all the days from the first day a statement was submitted to Learning Locker. + * + * @return $days number + * + **/ + private function statementDays(){ + $first_day = \DB::collection('statements')->first(); + if( $first_day ){ + $datetime1 = date_create( gmdate("Y-m-d", $first_day['created_at']->sec) ); + $datetime2 = date_create( gmdate("Y-m-d", time()) ); + $interval = date_diff($datetime1, $datetime2); + $days = $interval->days; + return $days; + }else{ + return ''; + } + } + + /** + * Using the number of days Learning Locker has been running with statements + * work out the average number of statements per day. + * + * @return $avg + * + **/ + public function statementAvgCount(){ + $count = $this->statementCount(); + $days = $this->statementDays(); + $avg = 0; + if( $count && $days ){ + $avg = round( $count / $days ); + } + return $avg; + } + + /** + * Using the number of days Learning Locker has been running with statements + * work out the average number of learners participating per day. + * + * @return $avg + * + **/ + public function learnerAvgCount(){ + $count = $this->actorCount(); + $days = $this->statementDays(); + $avg = 0; + if( $count && $days ){ + $avg = round( $count / $days ); + } + return $avg; + } + + /** + * Get a count of statements on each day Learning Locker has been active. + * + * @return $data json feed. + * + **/ + public function getStatementNumbersByDate(){ + + $statements = $this->db->statements->aggregate( + array('$group' => array('_id' => array('$dayOfYear' => '$created_at'), + 'count' => array('$sum' => 1), + 'date' => array('$addToSet' => '$stored'), + 'actor' => array('$addToSet' => '$actor'))), + array('$sort' => array('_id' => 1)), + array('$project' => array('count' => 1, 'date' => 1, 'actor' => 1)) + ); + //set statements for graphing + $data = ''; + foreach( $statements['result'] as $s ){ + $data .= json_encode( array( "y" => substr($s['date'][0],0,10), "a" => $s['count'], 'b' => count($s['actor'])) ) . ' '; + } + + return trim( $data ); + + } + +} \ No newline at end of file diff --git a/app/locker/data/Analytics.php b/app/locker/data/Analytics.php new file mode 100644 index 0000000000..5ac9b4dabf --- /dev/null +++ b/app/locker/data/Analytics.php @@ -0,0 +1,53 @@ +setDb(); + $this->lrs = $lrs; + $this->selectAnalytics( $segment ); + + } + + private function selectAnalytics( $segment ){ + + switch( $segment ){ + case 'verbCloud': + $this->verbCloud(); + case 'geo': + $this->geo(); + default: + $this->verbCloud(); + } + + } + + /** + * A simple verb cloud + * + **/ + public function verbCloud(){ + + $match = $this->getMatch( $this->lrs ); + $this->results['verbs'] = $this->db->statements->aggregate( + array('$match' => $match), + array('$group' => array('_id' => '$verb.id', + 'count' => array('$sum' => 1), + 'verb' => array('$addToSet' => '$verb.display.en-US'),)), + array('$sort' => array('count' => -1)), + array('$limit' => 6) + ); + + $this->results['verbs']['total'] = \Statement::where('context.extensions.http://learninglocker&46;net/extensions/lrs._id', $this->lrs)->remember(5)->count(); + + } + + public function geo(){ + + } + +} \ No newline at end of file diff --git a/app/locker/data/BaseData.php b/app/locker/data/BaseData.php new file mode 100644 index 0000000000..09f193c0b5 --- /dev/null +++ b/app/locker/data/BaseData.php @@ -0,0 +1,22 @@ +db = \DB::getMongoDB(); + } + + /** + * getMatch is used to match mongo aggregation searches to a specific LRS. + * + * @return array + * + **/ + protected function getMatch( $lrs ){ + return array('context.extensions.http://learninglocker&46;net/extensions/lrs._id' => $lrs); + } + +} + diff --git a/app/locker/data/Filter.php b/app/locker/data/Filter.php new file mode 100644 index 0000000000..c447406987 --- /dev/null +++ b/app/locker/data/Filter.php @@ -0,0 +1,56 @@ +data = $data; + $this->timeline(); + } + + public function timeline(){ + + $set_data = ''; + $count = 0; + $first = true; + $day = ''; + + foreach( $this->data as $d ){ + + $day = substr($d['stored'],0,10); + + if( $first ) { + $last_timestamp = $day; + } + + if($day != $last_timestamp && !$first ) { + + $set_data .= json_encode( array( "y" => $last_timestamp, "a" => $count ) ) . ' '; + $count = 0; + + } + + $count++; + $last_timestamp = $day; + $first = false; + + } + + $set_data .= json_encode( array( "y" => $day, "a" => $count ) ) . ' '; + + $this->timeline_data = trim( $set_data ); + + } + + private function count(){ + return count( $this->data ); + } + +} + diff --git a/app/locker/data/LrsDashboard.php b/app/locker/data/LrsDashboard.php new file mode 100644 index 0000000000..65623b69fc --- /dev/null +++ b/app/locker/data/LrsDashboard.php @@ -0,0 +1,172 @@ +setDb(); + + $this->lrs = $lrs; + + $this->setFullStats(); + + } + + /** + * Set all stats array. + * + **/ + public function setFullStats(){ + $this->stats = array('statement_count' => $this->statementCount(), + 'statement_avg' => $this->statementAvgCount(), + 'learner_avg' => $this->learnerAvgCount(), + 'statement_graph' => $this->getStatementNumbersByDate(), + 'actor_count' => $this->actorCount(), + 'source_count' => $this->sourceCount(), + ); + } + + /** + * Count all statements in Learning Locker + * + * @return count + * + **/ + public function statementCount(){ + return \DB::collection('statements') + ->where('context.extensions.http://learninglocker&46;net/extensions/lrs._id', $this->lrs) + ->remember(5) + ->count(); + } + + /** + * Count the number of distinct actors within LRS statements. + * @todo use more than just mbox + * + * @return count. + * + **/ + public function actorCount(){ + + $count = $this->db->statements->aggregate( + array('$match' => $this->getMatch( $this->lrs )), + array('$group' => array('_id' => '$actor.mbox')), + array('$group' => array('_id' => 1, 'count' => array('$sum' => 1))) + ); + + if( isset($count['result'][0]) ){ + return $count['result'][0]['count']; + }else{ + return 0; + } + + } + + /** + * Count the number of distinct activity ids. + * + * @return count. + * + **/ + public function sourceCount(){ + + $count = $this->db->statements->aggregate( + array('$match' => $this->getMatch( $this->lrs )), + array('$group' => array('_id' => '$object.id')), + array('$group' => array('_id' => 1, 'count' => array('$sum' => 1))) + ); + + if( isset($count['result'][0]) ){ + return $count['result'][0]['count']; + }else{ + return 0; + } + + } + + /** + * Get a count of all the days from the first day a statement was submitted to Lrs. + * + * @return $days number + * + **/ + private function statementDays(){ + $first_day = \DB::collection('statements')->first(); + if( $first_day ){ + $datetime1 = date_create( gmdate("Y-m-d", $first_day['created_at']->sec) ); + $datetime2 = date_create( gmdate("Y-m-d", time()) ); + $interval = date_diff($datetime1, $datetime2); + $days = $interval->days; + return $days; + }else{ + return ''; + } + } + + /** + * Using the number of days the LRS has been running with statements + * work out the average number of statements per day. + * + * @return $avg + * + **/ + public function statementAvgCount(){ + $count = $this->statementCount(); + $days = $this->statementDays(); + $avg = 0; + if( $count && $days ){ + $avg = round( $count / $days ); + } + return $avg; + } + + /** + * Using the number of days the LRS has been running with statements + * work out the average number of learners participating per day. + * + * @return $avg + * + **/ + public function learnerAvgCount(){ + $count = $this->actorCount(); + $days = $this->statementDays(); + $avg = 0; + if( $count && $days ){ + $avg = round( $count / $days ); + } + return $avg; + } + + /** + * Get a count of statements on each day the lrs has been active. + * + * @return $data json feed. + * + **/ + public function getStatementNumbersByDate(){ + + $statements = $this->db->statements->aggregate( + array('$match' => $this->getMatch( $this->lrs )), + array('$group' => array('_id' => array('$dayOfYear' => '$created_at'), + 'count' => array('$sum' => 1), + 'date' => array('$addToSet' => '$stored'), + 'actor' => array('$addToSet' => '$actor'))), + array('$sort' => array('_id' => 1)), + array('$project' => array('count' => 1, 'date' => 1, 'actor' => 1)) + ); + + //set statements for graphing + $data = ''; + foreach( $statements['result'] as $s ){ + //@todo check we are only counting Actor objectType 'Agent' and it is distinct + $data .= json_encode( array( "y" => substr($s['date'][0],0,10), "a" => $s['count'], 'b' => count($s['actor'])) ) . ' '; + } + + return trim( $data ); + + } + +} \ No newline at end of file diff --git a/app/locker/data/Reporting.php b/app/locker/data/Reporting.php new file mode 100644 index 0000000000..78f7c07974 --- /dev/null +++ b/app/locker/data/Reporting.php @@ -0,0 +1,5 @@ +comments = $comments; + + } + + public function stats(){ + + $count = $this->count( $this->comments ); + $learners = $this->learners( $this->comments ); + + return array( 'count' => $count, + 'learners' => $learners ); + + } + + private function count( $statements ){ + return count( $statements ); + } + + private function learners( $statements ){ + return 0; + } + +} \ No newline at end of file diff --git a/app/locker/helpers/Access.php b/app/locker/helpers/Access.php new file mode 100644 index 0000000000..f418854286 --- /dev/null +++ b/app/locker/helpers/Access.php @@ -0,0 +1,21 @@ +role == $role ){ + return true; + } + + return false; + + } + +} \ No newline at end of file diff --git a/app/locker/helpers/Helpers.php b/app/locker/helpers/Helpers.php new file mode 100644 index 0000000000..0aad5d0737 --- /dev/null +++ b/app/locker/helpers/Helpers.php @@ -0,0 +1,103 @@ + $value){ + + if(is_array($value)){ + + $output[$key] = Helpers::replaceFullStop( $value ); + + }else{ + + $new = Helpers::replaceFullStopInKeys( $key ); + $output[$new] = $value; + + } + + } + + return $output; + + + } + + /* + |--------------------------------------------------------------------------- + | Loop through a statement and check for NULL values. + |--------------------------------------------------------------------------- + */ + static function checkForNull( $array ){ + + foreach ( $array as $key => $value ){ + if( is_array( $value ) ){ + Helpers::checkForNull( $value ); + }else{ + if( !is_null($value) ){ + //if the key is not extensions, then reject statement + //do something + } + } + } + + } + + /* + |---------------------------------------------------------------------------- + | Generate a random key + |---------------------------------------------------------------------------- + */ + static function getRandomValue(){ + return sha1(uniqid(mt_rand(), true)); + } + + /* + |--------------------------------------------------------------------------- + | Get gravatar + |--------------------------------------------------------------------------- + */ + + static function getGravatar( $email, $size = '50' ){ + return "http://www.gravatar.com/avatar/" . md5( strtolower( trim( $email ) ) ) . "?s=" . $size; + } + +} \ No newline at end of file diff --git a/app/locker/helpers/Lrs.php b/app/locker/helpers/Lrs.php new file mode 100644 index 0000000000..b51a388f38 --- /dev/null +++ b/app/locker/helpers/Lrs.php @@ -0,0 +1,83 @@ +role, $site->create_lrs)){ + return true; + } + + return false; + + } + + /** + * @param $lrs Can the current user access based on passed role requirement + * + * @return boolean + **/ + public static function lrsAdmin( $lrs ){ + + $user = \Auth::user(); + + //get all users with access to the lrs + foreach( $lrs->users as $u ){ + $get_users[] = $u['_id']; + } + //check current user is in the list of allowed users and is an admin + if( !in_array($user->_id, $get_users) && $user->role == 'admin' ){ + return true; + } + + return false; + + } + + /** + * @param $lrs Can the current user edit lrs + * + * @return boolean + **/ + public static function lrsEdit( $lrs ){ + + $user = \Auth::user(); + + //get all users with access to the lrs + foreach( $lrs->users as $u ){ + $get_users[] = $u['_id']; + } + + //check current user is in the list of allowed users and is an admin + if( !in_array($user->_id, $get_users) && $user->role == 'admin' || $user->role == 'super' ){ + return true; + } + + return false; + + } + + /** + * Is user the owner of LRS (or site super admin) + * + * @return boolean + * + **/ + public static function lrsOwner( $lrs_id ){ + $lrs = \Lrs::find( $lrs_id ); + if( $lrs->owner['_id'] == \Auth::user()->_id || \Auth::user()->role == 'super' ){ + return true; + }else{ + return false; + } + } + + +} \ No newline at end of file diff --git a/app/locker/helpers/User.php b/app/locker/helpers/User.php new file mode 100644 index 0000000000..20a72c7016 --- /dev/null +++ b/app/locker/helpers/User.php @@ -0,0 +1,105 @@ +insert( + array('email' => $email, 'token' => $token) + ); + return $token; + + } + + /** + * This is used for the primary email when the user creates an account. + **/ + public static function sendEmailValidation( $user ){ + + $data = array('token' => User::setEmailToken( $user, $user->email )); + + \Mail::send('emails.verify', $data, function($message) use ($user){ + $message->to($user->email, $user->name)->subject('Welcome, please verify your email'); + }); + + } + + /** + * Invite in a user. + **/ + public static function inviteUser( $data ){ + + //explode email addresses + $emails = explode(',', $data['emails']); + + foreach( $emails as $e ){ + + //check it is a valid email address + if ( filter_var($e, FILTER_VALIDATE_EMAIL) ){ + + //does the user already exist? If so, skip next step + $user = \User::where('email', $e)->first(); + $user_exists = false; //boolean used to determine if add to lrs email sent + + if( !$user ){ + + //create a user account + $user = new \User; + $user->name = $e; + $user->email = $e; + $user->verified = 'no'; + $user->role = $data['role'] ? $data['role'] : 'observer'; + $user->password = \Hash::make(base_convert(uniqid('pass', true), 10, 36)); + $user->save(); + + //set data to use in email + $set_data = array('token' => User::setEmailToken( $user, $user->email ), + 'custom_message' => $data['message'], + 'sender' => \Auth::user()); + + //send out message to user + \Mail::send('emails.invite', $set_data, function($message) use ($user){ + $message->to($user->email, $user->name)->subject('You have been invited to join our LRS.'); + }); + + }else{ + $user_exists = true; + } + + //was an LRS id passed? If so, add user to that LRS as an observe + if( isset($data['lrs']) ){ + + $lrs = \Lrs::find( $data['lrs'] ); + + if( $lrs ){ + $existing = $lrs->users; + array_push($existing, array('_id' => $user->_id, + 'email' => $user->email, + 'role' => 'observer' )); + $lrs->users = $existing; + $lrs->save(); + } + + if( $user_exists ){ + //set data to use in email + $set_data = array('sender' => \Auth::user(), 'lrs' => $lrs); + //send out message to user + \Mail::send('emails.lrsInvite', $set_data, function($message) use ($user){ + $message->to($user->email, $user->name)->subject('You have been added to an LRS.'); + }); + } + + } + + } + + } + + } + +} \ No newline at end of file diff --git a/app/locker/listeners/LrsHandler.php b/app/locker/listeners/LrsHandler.php new file mode 100644 index 0000000000..bf32ac4c9a --- /dev/null +++ b/app/locker/listeners/LrsHandler.php @@ -0,0 +1,18 @@ +role == 'observer' ){ + $user->role = 'admin'; + $user->save(); + } + + } + +} \ No newline at end of file diff --git a/app/locker/listeners/RegisterHandler.php b/app/locker/listeners/RegisterHandler.php new file mode 100644 index 0000000000..e8c0c87737 --- /dev/null +++ b/app/locker/listeners/RegisterHandler.php @@ -0,0 +1,64 @@ +name = ''; + $site->description = ''; + $site->email = $user->email; + $site->lang = 'en-US'; + $site->create_lrs = array('super'); + $site->api = 'Closed'; + $site->registration = 'Closed'; + $site->claim = 'No'; //can learners claim statements? + $site->restrict = 'None'; //restrict registration to a specific email domain + $site->super = array( array('user' => $user->_id ) ); + $site->save(); + } + + //now send an email asking to verify email + $this->sendEmail( $user ); + + } + + public function domain_check( $data ){ + + $site = \Site::first(); + + //has a domain been set? + if( $site ){ + $domain = $site->domain; + if( $site->domain != '' ){ + $allowed_domain = array($domain); + // Make sure the address is valid + if ( filter_var($data['email'], FILTER_VALIDATE_EMAIL) ){ + + //get submitted email domain + $email = explode('@', $data['email']); + $email = array_pop( $email ); + + if ( !in_array($email, $allowed_domain) ){ + return false; + } + + } + } + } + + return true; + + } + + public function resentEmailVerification($user){ + $this->sendEmail( $user ); + } + + private function sendEmail( $user ){ + \app\locker\helpers\User::sendEmailValidation( $user ); + } + +} \ No newline at end of file diff --git a/app/locker/repository/Lrs/EloquentLrsRepository.php b/app/locker/repository/Lrs/EloquentLrsRepository.php new file mode 100644 index 0000000000..01427e2c42 --- /dev/null +++ b/app/locker/repository/Lrs/EloquentLrsRepository.php @@ -0,0 +1,99 @@ +lrs = $lrs; + } + + public function all(){ + if( \Auth::user()->role == 'super' ){ + return $this->lrs->all(); + }else{ + return $this->lrs->where('users._id', \Auth::user()->_id)->get(); + } + + } + + public function find($id){ + return $this->lrs->find($id); + } + + public function validate($data){ + $lrs = new Lrs; + return $lrs->validate( $data ); + } + + public function create( $input ){ + + $user = \Auth::user(); + $lrs = new Lrs; + $lrs->title = $input['title']; + $lrs->description = $input['description']; + $lrs->api = array('basic_key' => \app\locker\helpers\Helpers::getRandomValue(), + 'basic_secret' => \app\locker\helpers\Helpers::getRandomValue()); + $lrs->owner = array( '_id' => \Auth::user()->_id ); + $lrs->users = array( array('_id' => $user->_id, + 'email' => $user->email, + 'name' => $user->name, + 'role' => 'admin' ) ); + + $lrs->save() ? $result = true : $return = false; + + //fire a create lrs event if it worked and saced + if( $result ) + \Event::fire('user.create_lrs', array('user' => $user)); + + return $result; + + } + + public function update($id, $input){ + + $lrs = $this->find($id); + + $lrs->title = $input['title']; + $lrs->description = $input['description']; + + $lrs->save(); + + return $lrs; + + } + + public function delete($id){ + + $lrs = $this->find($id); + + //first delete all statements + + //now delete the lrs + return $lrs->delete(); + } + + public function statements( $id ){ + + return \Statement::where('context.extensions.http://learninglocker&46;net/extensions/lrs._id', $id) + ->orderBy('created_at', 'desc') + ->paginate(15); + + } + + public function removeUser( $id, $user ){ + return \DB::table('lrs')->where('_id', $id)->pull('users', array('_id' => $user)); + } + +} \ No newline at end of file diff --git a/app/locker/repository/Lrs/LrsRepository.php b/app/locker/repository/Lrs/LrsRepository.php new file mode 100644 index 0000000000..43d4a98f28 --- /dev/null +++ b/app/locker/repository/Lrs/LrsRepository.php @@ -0,0 +1,19 @@ +app->bind( + 'Locker\Repository\User\UserRepository', + 'Locker\Repository\User\EloquentUserRepository' + ); + $this->app->bind( + 'Locker\Repository\Statement\StatementRepository', + 'Locker\Repository\Statement\EloquentStatementRepository' + ); + $this->app->bind( + 'Locker\Repository\Lrs\LrsRepository', + 'Locker\Repository\Lrs\EloquentLrsRepository' + ); + $this->app->bind( + 'Locker\Repository\Site\SiteRepository', + 'Locker\Repository\Site\EloquentSiteRepository' + ); + } + + +} \ No newline at end of file diff --git a/app/locker/repository/Site/EloquentSiteRepository.php b/app/locker/repository/Site/EloquentSiteRepository.php new file mode 100644 index 0000000000..c73fe9622f --- /dev/null +++ b/app/locker/repository/Site/EloquentSiteRepository.php @@ -0,0 +1,82 @@ +first(); + } + + public function find($id){ + return Site::find($id); + } + + public function validate($data){ + $site = new Site; + return $site->validate( $data ); + } + + public function create( $data ){ + + //check site has not already been set + + $site = new Site; + $site->name = $data['name']; + $site->description = $data['description']; + $site->email = $data['email']; + $site->lang = $data['lang']; + $site->create_lrs = array('super'); + $site->api = $data['api']; + $site->registration = $data['registration']; + $site->claim = $data['claim']; //can learners claim statements? + $site->restrict = $data['restrict']; //restrict registration to a specific email domain + $site->super = array( array('user' => \Auth::user()->_id ) ); + $site->save(); + + return $site; + //return User::create($input); + } + + public function update($id, $data){ + + $site = $this->find( $id ); + $site->name = $data['name']; + $site->description = $data['description']; + $site->email = $data['email']; + $site->lang = $data['lang']; + if( isset($data['create_lrs']) ) + $site->create_lrs = array_merge(array('super'), $data['create_lrs']); + $site->api = $data['api']; + $site->registration = $data['registration']; + $site->claim = $data['claim']; //can learners claim statements? + $site->domain = $data['domain']; //restrict registration to a specific email domain + return $site->save(); + + } + + public function delete($id){ + + $site = $this->find($id); + return $site->delete(); + } + + public function verifyUser( $user_id ){ + //check user exists + $user = \User::find( $user_id ); + if( $user ){ + $user->verified = 'yes'; + $user->save(); + } + return $user; + } + + public function inviteUsers( $data ){ + + if( $data ){ + \app\locker\helpers\User::inviteUser( $data ); + } + + } + +} \ No newline at end of file diff --git a/app/locker/repository/Site/SiteRepository.php b/app/locker/repository/Site/SiteRepository.php new file mode 100644 index 0000000000..7a863875a4 --- /dev/null +++ b/app/locker/repository/Site/SiteRepository.php @@ -0,0 +1,19 @@ +runValidation(); + + //if the statement does not validate, return with errors + if( $return['status'] == 'failed' ){ + return array( 'success' => false, + 'message' => $return['errors']); + } + + //statement has validated, so continue with verified statement. + $vs = $return['statement']; + + + /* + |------------------------------------------------------------------------------ + | Add the correct learning locker LRS. + |------------------------------------------------------------------------------ + */ + $vs['context']['extensions']['http://learninglocker&46;net/extensions/lrs'] = array( '_id' => $lrs->_id, + 'name' => $lrs->title ); + + + /* + |------------------------------------------------------------------------------ + | The date stored in LRS in ISO 8601 format + |------------------------------------------------------------------------------ + */ + $vs['stored'] = date('c'); + + + /* + |------------------------------------------------------------------------------ + | Check to see if the object is an activity, if so, check to see if that + | activity is already in the DB. if it is, use the stored version. + | If not, store it. + |------------------------------------------------------------------------------ + */ + $vs['object']['definition'] = $this->saveActivity( $vs['object']['id'], $vs['object']['definition'] ); + + + /* + |------------------------------------------------------------------------------ + | Run through keys to make sure there are no full stops. If so, replace with + | html entity &46; - this will probably only occur in extensions. + |------------------------------------------------------------------------------ + */ + $vs = $this->replaceFullStop( $vs ); + + + /* + |------------------------------------------------------------------------------- + | Create a new statement object + |------------------------------------------------------------------------------- + */ + $new_statement = new Statement; + $new_statement->fill( $vs ); + + if( $new_statement->save() ){ + return array( 'success' => true, + 'id' => $new_statement->_id ); + } + + return array( 'success' => false, + 'message' => $new_statement->errors ); + + } + + + /* + |------------------------------------------------------------------------------- + | Each verb has a category in Learning Locker. Grab the category. + |------------------------------------------------------------------------------- + */ + private function getCategory( $verb ){ + return \app\locker\helpers\Helpers::getVerbCategory( $verb ); + } + + + /* + |------------------------------------------------------------------------------- + | Save object type activity for reference. + |------------------------------------------------------------------------------- + */ + private function saveActivity( $activity_id, $activity_def ){ + + $exists = \DB::table('activities')->find( $activity_id ); + + //if the object activity exists, return details on record. + if( $exists ){ + return $exists['definition']; + }else{ + //save it + \DB::table('activities')->insert( + array('_id' => $activity_id, + 'definition' => $activity_def) + ); + return $activity_def; + } + + } + + + /* + |------------------------------------------------------------------------------- + | Mongo doesn't allow full stops (.) in keys as it is reserved, so, + | we replace with &46; where required. This will most likely only + | happen in extensions. + |------------------------------------------------------------------------------- + */ + private function replaceFullStop( $statement ){ + + $statement = \app\locker\helpers\Helpers::replaceFullStop( $statement ); + return $statement; + + } + + /* + |---------------------------------------------------------------------- + | Filter statements via our filtering options. + | + | @param $id The LRS unique id. + | @param $vars array An array or parameters grabbed from the url. + | + | @return array - an array containing statements for display and data + | for the graph. + |---------------------------------------------------------------------- + */ + public function filter( $id, $vars, $restrict='' ){ + + $filter = array(); + $data = ''; + + //create key / value array for wheres from $vars sent over + if( isset($vars) && !empty($vars) ){ + while (count($vars)) { + list($key,$value) = array_splice($vars, 0, 2); + $filter[$key] = $value; + } + } + + $query = \Statement::where('context.extensions.http://learninglocker&46;net/extensions/lrs._id', $id); + $this->setRestriction( $restrict, $query ); + if( !empty($filter) ){ + $this->setWhere( $filter, $query ); + } + $query->orderBy('created_at', 'desc'); + //$query->remember(5); + $statements = $query->paginate(18); + + + $query = \Statement::where('context.extensions.http://learninglocker&46;net/extensions/lrs._id', $id); + $this->setRestriction( $restrict, $query ); + if( !empty($filter) ){ + $this->setWhere( $filter, $query ); + } + $query->remember(5); + $data = $query->get(); + + return array( 'statements' => $statements, + 'data' => $data, + 'filter' => $filter ); + + } + + /* + |---------------------------------------------------------------------- + | + | Loop through passed parameters and add to DB query. + | + | @todo only decode on urls not everything + | + |---------------------------------------------------------------------- + */ + private function setWhere( $filter, $query ){ + + foreach( $filter as $k => $v ){ + $k = $this->filterKeyLookUp( $k ); + $query->where( $k, rawurldecode( $v ) ); + } + return $query; + + } + + /* + |---------------------------------------------------------------------- + | + | Set a restriction for the query if one was passed + | + |---------------------------------------------------------------------- + */ + private function setRestriction( $restriction, $query ){ + + if( $restriction != '' ){ + switch( $restriction ){ + case 'comments': + return $query->where( 'object.definition.type', 'http://activitystrea.ms/schema/1.0/comment' ); + case 'badges': + return $query->where( 'object.definition.type', 'http://activitystrea.ms/schema/1.0/comment' ); + case 'results': + return $query->where( 'object.definition.type', 'http://activitystrea.ms/schema/1.0/comment' ); + case 'courses': + return $query->where( 'object.definition.type', 'http://activitystrea.ms/schema/1.0/comment' ); + default: + return $query->where( 'object.definition.type', 'http://activitystrea.ms/schema/1.0/comment' ); + } + } + + } + + private function filterKeyLookUp( $key ){ + switch( $key ){ + case 'actor': + return 'actor.mbox'; + case 'verb': + return 'verb.display.en-US'; + case 'parent': + return 'context.contextActivities.parent.id'; + case 'course': + return 'context.contextActivities.grouping.id'; + case 'activity': + return 'object.id'; + } + } + +} \ No newline at end of file diff --git a/app/locker/repository/Statement/StatementRepository.php b/app/locker/repository/Statement/StatementRepository.php new file mode 100644 index 0000000000..98b983a348 --- /dev/null +++ b/app/locker/repository/Statement/StatementRepository.php @@ -0,0 +1,13 @@ +validate( $data ); + } + + public function create(){ + $get_users = $this->all(); + //if it is the first user, give all three roles, else, go with observer + if( count($get_users) == 0 ){ + $role = 'super'; //\app\locker\helpers\Access::roles(); + }else{ + $role = 'observer'; + } + $user = new User; + $user->name = \Input::get('name'); + $user->email = \Input::get('email'); + $user->verified = 'no'; + $user->role = $role; + $user->password = \Hash::make(\Input::get('password')); + + $user->save(); + + return $user; + + } + + public function update($id, $data){ + + $user = $this->find($id); + $user->name = $data['name']; + $user->email = $data['email']; + return $user->save(); + + } + + public function verifyEmail($token){ + + //first see if a record exists for that email and token + $email = \DB::table('user_tokens') + ->where('token', $token) + ->pluck('email'); + + if( $email ){ + //verify email + \User::where('email', $email)->update(array('verified' => 'yes')); + $message_type = 'success'; + $message = \Lang::get('users.email_verified'); + }else{ + $message_type = 'error'; + $message = \Lang::get('users.email_verified_error'); + } + + return $message; + + } + + public function updateRole( $user, $role ){ + + $user = $this->find( $user ); + $user->role = $role; + return $user->save(); + + } + + public function updatePassword($id, $password){ + + $user = $this->find($id); + $user->password = \Hash::make( $password ); + $user->save(); + return $user; + + } + + public function delete( $id ){ + + $user = $this->find($id); + + //@todo transfer all LRSs they own to the system admin + + //remove users from any LRSs + \DB::table('lrs')->pull('users', array('_id' => $user->_id)); + + //delete user document + return $user->delete(); + + } + +} \ No newline at end of file diff --git a/app/locker/repository/User/UserRepository.php b/app/locker/repository/User/UserRepository.php new file mode 100644 index 0000000000..89d62f0b6a --- /dev/null +++ b/app/locker/repository/User/UserRepository.php @@ -0,0 +1,21 @@ +simpleTable( $statement, $lrs ); + case 'comments': + return $this->comments( $statement, $lrs ); + case 'commentsActor': + return $this->commentsActor( $statement, $lrs ); + } + + } + + private function simpleTable( $statement, $lrs ){ + + $display = '
' . $badge->description . '
'; + $badge_display .= 'Issued by: ' . $issuer->name . '
'; + $badge_display .= 'To do this, please click on the following link: {{ URL::to('email/invite', array($token)) }}.
+To visit the LRS, click here {{ URL() }}/lrs/{{ $lrs->_id }}
+Thank's for signing up to use Learning Locker. To complete your registration, we need you to verify + your email.
+To do this, please click on the following link: {{ URL::to('email/verify', array($token)) }}.
+Count | +Verb | +% pop | +% all | +Info | +|
---|---|---|---|---|---|
+ + | ++ {{ $v['count'] }} + | ++ {{ $v['verb']['0'] }} + | ++ {{ $percent }}% + | ++ {{ $percent_overall }}% + | ++ + + + | +
This will be about this lrs's API.
+ +This will be details on this LRS's API.
+{{ Lang::get('lrs.verify', array('verify_link' => $verify_link)) }}
+ @endif ++ Your daily average is + {{ $stats['statement_avg'] }} {{ lcfirst(Lang::get('statements.statements')) }} with + {{ $stats['learner_avg'] }} learners participating. +
+ @include('partials.graphs.view.dashboard') +{{ Lang::get('lrs.endpoint.instructions') }}
+Coming soon.
+{{ Form::submit(Lang::get('site.submit'), array('class'=>'btn btn-primary')) }}
+{{ Form::submit(Lang::get('site.submit'), array('class'=>'btn btn-primary')) }}
+{{ Lang::get('lrs.none') }}
+All about Learning Locker.
+ +@stop \ No newline at end of file diff --git a/app/views/partials/pages/help.blade.php b/app/views/partials/pages/help.blade.php new file mode 100644 index 0000000000..d59caccf7c --- /dev/null +++ b/app/views/partials/pages/help.blade.php @@ -0,0 +1,15 @@ +@extends('layouts.master') + +@section('sidebar') + @include('layouts.sidebars.blank') +@stop + +@section('content') + +This will be help documents.
+ +@stop \ No newline at end of file diff --git a/app/views/partials/pages/terms.blade.php b/app/views/partials/pages/terms.blade.php new file mode 100644 index 0000000000..7cfe21b3ec --- /dev/null +++ b/app/views/partials/pages/terms.blade.php @@ -0,0 +1,13 @@ +@extends('layouts.master') + +@section('sidebar') + @include('layouts.sidebars.blank') +@stop + +@section('content') + +Your daily average is {{ $stats['statement_avg'] }} statements with + {{ $stats['learner_avg'] }} learners participating.
+ @include('partials.graphs.view.dashboard') +{{ Form::submit(Lang::get('site.submit'), array('class'=>'btn btn-primary')) }}
+{{ Lang::get('site.name') }} | +{{ $site->name }} | +
{{ Lang::get('site.description') }} | +{{ $site->description }} | +
{{ Lang::get('site.email') }} | +{{ $site->email }} | +
{{ Lang::get('site.language') }} | +{{ $site->lang }} | +
{{ Lang::get('site.create_lrs') }} | +@foreach( $site->create_lrs as $u ) {{ $u }}, @endforeach | +
{{ Lang::get('site.api_status') }} | +API {{ $site->api }} | +
{{ Lang::get('site.registration') }} | ++ + {{ $site->registration }} + + | +
{{ Lang::get('site.restrict') }} | +@if( $site->domain ) @ {{ $site->domain }} @else Not set @endif | +
{{Lang::get('site.claim')}} | +{{ $site->claim }} | +
This is a super simple tool to test statement creation. If there is a need for a full manual statement creation, we will provide more options.
+Put in some help text.
+Put in some help text.
+Put in some help text.
+Put in some help text.
+Put in some help text...
+Put in some help text.
+{{ Lang::get('users.password_instructions') }}
++ {{ Lang::get('users.email') }}: {{ $user->email }} + @if($user->verified == 'yes') + + {{ Lang::get('users.verified') }} + + @else + + {{ Lang::get('users.unverified') }} + + @include('partials.users.forms.resendEmailVerification') + @endif +
+{{ Lang::get('users.role') }}: {{ $user->role }}
+