openstax_connect 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +70 -0
  3. data/Rakefile +49 -0
  4. data/app/algorithms/openstax/connect/search_users.rb +47 -0
  5. data/app/assets/javascripts/openstax/connect/application.js +15 -0
  6. data/app/assets/javascripts/openstax/connect/sessions.js +2 -0
  7. data/app/assets/stylesheets/openstax/connect/application.css +13 -0
  8. data/app/assets/stylesheets/openstax/connect/dev.css.scss +31 -0
  9. data/app/assets/stylesheets/openstax/connect/sessions.css +4 -0
  10. data/app/controllers/openstax/connect/application_controller.rb +9 -0
  11. data/app/controllers/openstax/connect/dev/dev_controller.rb +16 -0
  12. data/app/controllers/openstax/connect/dev/sessions_controller.rb +21 -0
  13. data/app/controllers/openstax/connect/dev/users_controller.rb +28 -0
  14. data/app/controllers/openstax/connect/sessions_controller.rb +44 -0
  15. data/app/handlers/openstax/connect/dev/users_create.rb +31 -0
  16. data/app/handlers/openstax/connect/dev/users_generate.rb +36 -0
  17. data/app/handlers/openstax/connect/dev/users_search.rb +64 -0
  18. data/app/handlers/openstax/connect/sessions_omniauth_authenticated.rb +35 -0
  19. data/app/helpers/openstax/connect/application_helper.rb +20 -0
  20. data/app/helpers/openstax/connect/sessions_helper.rb +4 -0
  21. data/app/models/anonymous_user.rb +40 -0
  22. data/app/models/user.rb +21 -0
  23. data/app/views/layouts/openstax/connect/application.html.erb +14 -0
  24. data/app/views/openstax/connect/dev/users/index.html.erb +31 -0
  25. data/app/views/openstax/connect/dev/users/search.js.erb +21 -0
  26. data/app/views/openstax/connect/sessions/create.html.erb +10 -0
  27. data/app/views/openstax/connect/shared/_attention.html.erb +3 -0
  28. data/app/views/openstax/connect/users/_action_create_form.html.erb +9 -0
  29. data/app/views/openstax/connect/users/_action_dialog.html.erb +10 -0
  30. data/app/views/openstax/connect/users/_action_list.html.erb +33 -0
  31. data/app/views/openstax/connect/users/_action_search.html.erb +25 -0
  32. data/app/views/openstax/connect/users/action_search.js.erb +8 -0
  33. data/config/initializers/02_extend_builtins.rb +48 -0
  34. data/config/routes.rb +32 -0
  35. data/db/migrate/20130729213800_create_users.rb +9 -0
  36. data/db/migrate/20130909215452_add_fields_to_user.rb +11 -0
  37. data/lib/omniauth/strategies/openstax.rb +28 -0
  38. data/lib/openstax/connect/action_list.rb +40 -0
  39. data/lib/openstax/connect/engine.rb +34 -0
  40. data/lib/openstax/connect/route_helper.rb +34 -0
  41. data/lib/openstax/connect/utilities.rb +12 -0
  42. data/lib/openstax/connect/version.rb +5 -0
  43. data/lib/openstax_connect.rb +72 -0
  44. data/lib/tasks/connect-rails_tasks.rake +4 -0
  45. metadata +191 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ connect-rails
2
+ =============
3
+
4
+ A rails engine for interfacing with OpenStax's common services server.
5
+
6
+ Usage
7
+ -----
8
+
9
+ Add the engine to your Gemfile and then run `bundle install`.
10
+
11
+ Mount the engine your application's `routes.rb` file:
12
+
13
+ MyApplication::Application.routes.draw do
14
+ ...
15
+ mount OpenStax::Connect::Engine, at: "/connect"
16
+ ...
17
+ end
18
+
19
+ You can use whatever path you want instead of `/connect`, just make sure to make the appropriate changes below.
20
+
21
+ Create an `openstax_connect.rb` initializer in `config/initializers`, with the following contents:
22
+
23
+ OpenStax::Connect.configure do |config|
24
+ config.openstax_application_id = 'value_from_openstax_services_here'
25
+ config.openstax_application_secret = 'value_from_openstax_services_here'
26
+ end
27
+
28
+ If you're running OpenStax Services in a dev instance on your machine, you can specify that instance's local URL with:
29
+
30
+ config.openstax_services_url = 'http://localhost:2999/'
31
+
32
+ To have users login, direct them to `/connect/sessions/new`. This is also available through the `openstax_connect.login` route helper, e.g. `<%= link_to 'Sign in!', openstax_connect.login_path %>`.
33
+
34
+ There is also a logout path helper for `/connect/sessions/destroy`, given by `logout_path`. By default this expects a `GET` request. If you'd prefer a `DELETE` request, add this configuration:
35
+
36
+ config.logout_via = :delete
37
+
38
+ Make sure to install the engine's migrations:
39
+
40
+ rake openstax_connect:install:migrations
41
+
42
+ Example Application
43
+ -------------------
44
+
45
+ There is an example application included in the gem in the `example` folder. Here are steps
46
+ to follow:
47
+
48
+ 1. Download (clone) the OpenStax Services site from github.com/openstax/services.
49
+ 1. In the site's `config` folder put a `secret_settings.yml` file that has values for the
50
+ following keys: `facebook_app_id`, `facebook_app_secret`, `twitter_consumer_key`, `twitter_consumer_secret`. If you
51
+ don't have access to these values, you can always make dummy apps on facebook and twitter.
52
+ 2. Do the normal steps to get this site online:
53
+ 1. Run `bundle install --without production`
54
+ 2. Run `bundle exec rake db:migrate`
55
+ 3. Run `bundle exec rails server`
56
+ 2. Open this services site in a web browser (defaults to http://localhost:2999)
57
+ 3. Navigate to http://localhost:2999/oauth/applications
58
+ 4. Click `New application`
59
+ 5. Set the callback URL to `http://localhost:4000/connect/auth/openstax/callback`.
60
+ Port 4000 is where you'll be running the example application.
61
+ 1. The name can be whatever.
62
+ 2. Click the `Trusted?` checkbox.
63
+ 3. Click `Submit`.
64
+ 4. Keep this window open so you can copy the application ID and secret into the example app
65
+ 5. Leave the services app running
66
+ 6. Download (clone) the OpenStax Connect gem from github.com/openstax/connect-rails. The
67
+ example application is in the `example` folder. In that folder's config folder, create a `secret_settings.yml` file
68
+ according to the instructions in `secret_settings.yml.example`. Run the example server in the normal way (bundle install..., migrate db, rails server).
69
+ 7. Navigate to the home page, http://localhost:4000. Click log in and play around. You can also refresh the services site and see yourself logged in, log out, etc.
70
+ 8. For fun, change example/config/openstax_connect.rb to set `enable_stubbing` to `true`. Now when you click login you'll be taken to a developer-only page where you can login as other users, generate new users, create new users, etc.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ # begin
6
+ # require 'bundler/setup'
7
+ # rescue LoadError
8
+ # puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
9
+ # end
10
+ # begin
11
+ # require 'rdoc/task'
12
+ # rescue LoadError
13
+ # require 'rdoc/rdoc'
14
+ # require 'rake/rdoctask'
15
+ # RDoc::Task = Rake::RDocTask
16
+ # end
17
+
18
+ # RDoc::Task.new(:rdoc) do |rdoc|
19
+ # rdoc.rdoc_dir = 'rdoc'
20
+ # rdoc.title = 'OpenstaxConnect'
21
+ # rdoc.options << '--line-numbers'
22
+ # rdoc.rdoc_files.include('README.rdoc')
23
+ # rdoc.rdoc_files.include('lib/**/*.rb')
24
+ # end
25
+
26
+ # APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
27
+ # load 'rails/tasks/engine.rake'
28
+
29
+
30
+
31
+ # Bundler::GemHelper.install_tasks
32
+
33
+ # require 'rake/testtask'
34
+
35
+ # Rake::TestTask.new(:test) do |t|
36
+ # t.libs << 'lib'
37
+ # t.libs << 'test'
38
+ # t.pattern = 'test/**/*_test.rb'
39
+ # t.verbose = false
40
+ # end
41
+
42
+
43
+ # task :default => :test
44
+
45
+ require 'rspec/core/rake_task'
46
+
47
+ RSpec::Core::RakeTask.new('spec')
48
+
49
+ task :default => :spec
@@ -0,0 +1,47 @@
1
+ module OpenStax::Connect
2
+
3
+ class SearchUsers
4
+ include Lev::Algorithm
5
+
6
+ protected
7
+
8
+ def exec(terms, type=:any)
9
+ # Return empty results if no search terms
10
+ return User.where{id == nil}.where{id != nil} if terms.blank?
11
+
12
+ # Note: % is the wildcard. This allows the user to search
13
+ # for stuff that "begins with" but not "ends with".
14
+ case type
15
+ when :name
16
+ users = User.scoped
17
+ terms.gsub(/[%,]/, '').split.each do |t|
18
+ next if t.blank?
19
+ query = t + '%'
20
+ users = users.where{(first_name =~ query) | (last_name =~ query)}
21
+ end
22
+ when :username
23
+ query = terms.gsub('%', '') + '%'
24
+ users = where{username =~ query}
25
+ when :any
26
+ users = User.scoped
27
+ terms.gsub(/[%,]/, '').split.each do |t|
28
+ next if t.blank?
29
+ query = t + '%'
30
+ users = users.where{(first_name =~ query) |
31
+ (last_name =~ query) |
32
+ (username =~ query)}
33
+ end
34
+ else
35
+ raise IllegalArgument, "Unknown user search type: #{type.to_s}"
36
+ end
37
+
38
+ return users
39
+ end
40
+
41
+ def self.default_transaction_isolation
42
+ Lev::TransactionIsolation.serializable
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,31 @@
1
+ @mixin section-block-base($heading_font_size) {
2
+ margin: 5px 0px;
3
+
4
+ .section-block-heading {
5
+
6
+ color: black;
7
+ font-size: $heading_font_size;
8
+
9
+ a {
10
+ font-size: $heading_font_size;
11
+ }
12
+ }
13
+
14
+ .section-block-body {
15
+ margin: $heading_font_size/2 0px 0px;
16
+ padding: 0px 0px 0px $heading_font_size;
17
+ }
18
+ }
19
+
20
+ .section-block-section {
21
+ @include section-block-base(22px);
22
+ }
23
+
24
+
25
+ .section-block-section.nesting-2 {
26
+ @include section-block-base(14px);
27
+
28
+ .section-block-heading {
29
+ font-style: italic;
30
+ }
31
+ }
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,9 @@
1
+ # References:
2
+ # http://edgeguides.rubyonrails.org/engines.html#using-a-class-provided-by-the-application
3
+
4
+ # Inherit from the applications ApplicationController to share some methods
5
+ class OpenStax::Connect::ApplicationController < ApplicationController
6
+
7
+ include Lev::HandleWith
8
+
9
+ end
@@ -0,0 +1,16 @@
1
+ require_dependency "openstax/connect/application_controller"
2
+
3
+ module OpenStax
4
+ module Connect
5
+ module Dev
6
+ class DevController < ApplicationController
7
+
8
+ before_filter Proc.new{
9
+ raise SecurityTransgression unless !Rails.env.production? ||
10
+ current_user.is_administrator?
11
+ }
12
+
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ module OpenStax
2
+ module Connect
3
+ module Dev
4
+ class SessionsController < DevController
5
+
6
+ # def new
7
+
8
+ # end
9
+
10
+ # def create; end
11
+
12
+ # def search
13
+ # handle_with(Dev::SessionsUserSearch,
14
+ # params: params,
15
+ # complete: lambda { render 'search' })
16
+ # end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ module OpenStax
2
+ module Connect
3
+ module Dev
4
+ class UsersController < DevController
5
+
6
+ def index; end
7
+
8
+ def create
9
+ handle_with(Dev::UsersCreate,
10
+ success: lambda { redirect_to dev_users_path, notice: 'Success!'},
11
+ failure: lambda { render 'index', alert: 'Error' })
12
+ end
13
+
14
+ def generate
15
+ handle_with(Dev::UsersGenerate,
16
+ success: lambda { redirect_to dev_users_path, notice: 'Success!'},
17
+ failure: lambda { render 'index', alert: 'Error' })
18
+ end
19
+
20
+ def search
21
+ handle_with(Dev::UsersSearch,
22
+ complete: lambda { render 'search' })
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ require_dependency "openstax/connect/application_controller"
2
+
3
+ module OpenStax
4
+ module Connect
5
+ class SessionsController < ApplicationController
6
+
7
+ def new
8
+ session[:return_to] = request.referrer
9
+ redirect_to RouteHelper.get_path(:login)
10
+ end
11
+
12
+ def omniauth_authenticated
13
+ handle_with(SessionsOmniauthAuthenticated,
14
+ complete: lambda {
15
+ sign_in(@results[:user_to_sign_in])
16
+ redirect_to return_path(true)
17
+ })
18
+ end
19
+
20
+ def destroy
21
+ sign_out!
22
+ redirect_to return_path, notice: "Signed out!"
23
+ end
24
+
25
+ def failure
26
+ redirect_to return_path, alert: "Authentication failed, please try again."
27
+ end
28
+
29
+ def become
30
+ raise SecurityTransgression unless !Rails.env.production? || current_user.is_administrator?
31
+ sign_in(User.find(params[:user_id]))
32
+ redirect_to return_path(true)
33
+ end
34
+
35
+ protected
36
+
37
+ def return_path(include_referrer=false)
38
+ referrer = include_referrer ? request.referrer : nil
39
+ params[:return_to] || session.delete(:return_to) || referrer || main_app.root_path
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+ class OpenStax::Connect::Dev::UsersCreate
2
+ include Lev::Handler
3
+
4
+ protected
5
+
6
+ def setup
7
+ end
8
+
9
+ def authorized?
10
+ !Rails.env.production?
11
+ end
12
+
13
+ def exec
14
+ u = User.create do |user|
15
+ user.first_name = params[:register][:first_name]
16
+ user.last_name = params[:register][:last_name]
17
+ user.username = params[:register][:username]
18
+ user.is_administrator = params[:register][:is_administrator]
19
+ user.openstax_uid = available_openstax_uid
20
+ end
21
+
22
+ transfer_errors_from(u, :register)
23
+
24
+ results[:user] = u
25
+ end
26
+
27
+ def available_openstax_uid
28
+ (User.order("openstax_uid DESC").first.try(:openstax_uid) || 0) + 1
29
+ end
30
+
31
+ end
@@ -0,0 +1,36 @@
1
+ module OpenStax::Connect::Dev
2
+ class UsersGenerate
3
+ include Lev::Handler
4
+
5
+ protected
6
+
7
+ paramify :generate do
8
+ attribute :count, type: Integer
9
+ validates :count, numericality: { only_integer: true,
10
+ greater_than_or_equal_to: 0 }
11
+ end
12
+
13
+ def authorized?
14
+ !Rails.env.production?
15
+ end
16
+
17
+ def exec
18
+ generate_params.count.times do
19
+ while !(User.where(:username => (username = SecureRandom.hex(4))).empty?) do; end
20
+
21
+ u = User.create do |user|
22
+ user.first_name = "Jane#{username}"
23
+ user.last_name = "Doe#{username}"
24
+ user.username = username
25
+ user.is_administrator = false
26
+ user.openstax_uid = available_openstax_uid
27
+ end
28
+ end
29
+ end
30
+
31
+ def available_openstax_uid
32
+ (User.order("openstax_uid DESC").first.try(:openstax_uid) || 0) + 1
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,64 @@
1
+ module OpenStax::Connect::Dev
2
+ class UsersSearch
3
+
4
+ include Lev::Handler
5
+
6
+ paramify :search do
7
+ attribute :search_type, type: String
8
+ validates :search_type, presence: true,
9
+ inclusion: { in: %w(Name Username Any),
10
+ message: "is not valid" }
11
+
12
+ attribute :search_terms, type: String
13
+ validates :search_terms, presence: true
14
+ end
15
+
16
+ uses_routine OpenStax::Connect::SearchUsers, as: :search_users
17
+
18
+ protected
19
+
20
+ def authorized?
21
+ !Rails.env.production? || caller.is_administrator?
22
+ end
23
+
24
+ def exec
25
+ terms = search_params.search_terms
26
+ type = search_params.search_type
27
+
28
+ # results[:users] = run(OpenStax::Connect::SearchUsers, terms, type.downcase.to_sym)
29
+ # results[:users] = run(:openstax_connect_search_users, terms, type.downcase.to_sym)
30
+ results[:users] = run(:search_users, terms, type.downcase.to_sym)
31
+
32
+ # if terms.blank?
33
+ # users = User.where{id == nil}.where{id != nil} # Empty
34
+ # else
35
+ # # Note: % is the wildcard. This allows the user to search
36
+ # # for stuff that "begins with" but not "ends with".
37
+ # case type
38
+ # when 'Name'
39
+ # users = User.scoped
40
+ # terms.gsub(/[%,]/, '').split.each do |t|
41
+ # next if t.blank?
42
+ # query = t + '%'
43
+ # users = users.where{(first_name =~ query) | (last_name =~ query)}
44
+ # end
45
+ # when 'Username'
46
+ # query = terms.gsub('%', '') + '%'
47
+ # users = where{username =~ query}
48
+ # else # Any
49
+ # users = User.scoped
50
+ # terms.gsub(/[%,]/, '').split.each do |t|
51
+ # next if t.blank?
52
+ # query = t + '%'
53
+ # users = users.where{(first_name =~ query) |
54
+ # (last_name =~ query) |
55
+ # (username =~ query)}
56
+ # end
57
+ # end
58
+ # end
59
+
60
+ # results[:users] = users
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ module OpenStax::Connect
2
+
3
+ class SessionsOmniauthAuthenticated
4
+ include Lev::Handler
5
+
6
+ protected
7
+
8
+ def setup
9
+ @auth_data = request.env['omniauth.auth']
10
+ end
11
+
12
+ def authorized?
13
+ @auth_data.provider == "openstax"
14
+ end
15
+
16
+ def exec
17
+ results[:user_to_sign_in] = user_to_sign_in
18
+ end
19
+
20
+ def user_to_sign_in
21
+ return caller if
22
+ !caller.nil? &&
23
+ !caller.is_anonymous? &&
24
+ caller.openstax_uid == @auth_data.uid
25
+
26
+ existing_user = User.where(openstax_uid: @auth_data.uid).first
27
+ return existing_user if !existing_user.nil?
28
+
29
+ return User.create do |user|
30
+ user.openstax_uid = @auth_data.uid
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,20 @@
1
+ module OpenStax
2
+ module Connect
3
+ module ApplicationHelper
4
+
5
+ def unless_errors(options={}, &block)
6
+ options[:errors] ||= @errors
7
+ options[:errors_html_id] ||= OpenStax::Connect.configuration.default_errors_html_id
8
+ options[:errors_partial] ||= OpenStax::Connect.configuration.default_errors_partial
9
+ options[:trigger] ||= OpenStax::Connect.configuration.default_errors_added_trigger
10
+
11
+ if options[:errors].any? || flash[:alert]
12
+ "$('##{options[:errors_html_id]}').html('#{ j(render options[:errors_partial], errors: options[:errors]) }').trigger('#{options[:trigger]}');".html_safe
13
+ else
14
+ ("$('##{options[:errors_html_id]}').html('');" + capture(&block)).html_safe
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ module OpenStax::Connect
2
+ module SessionsHelper
3
+ end
4
+ end
@@ -0,0 +1,40 @@
1
+ require 'singleton'
2
+
3
+ class AnonymousUser
4
+ include Singleton
5
+
6
+ def is_administrator?
7
+ false
8
+ end
9
+
10
+ def is_anonymous?
11
+ true
12
+ end
13
+
14
+ def id
15
+ nil
16
+ end
17
+
18
+ # Necessary if an anonymous user ever runs into an Exception
19
+ # or else the developer email doesn't work
20
+ def username
21
+ 'anonymous'
22
+ end
23
+
24
+ def first_name
25
+ 'Guest User'
26
+ end
27
+
28
+ def last_name
29
+ 'Guest User'
30
+ end
31
+
32
+ def name
33
+ 'Guest User'
34
+ end
35
+
36
+ def openstax_uid
37
+ nil
38
+ end
39
+
40
+ end
@@ -0,0 +1,21 @@
1
+ class User < ActiveRecord::Base
2
+
3
+ validates :username, uniqueness: true
4
+ validates :username, presence: true
5
+ validates :openstax_uid, presence: true
6
+ validates :first_name, presence: true
7
+ validates :last_name, presence: true
8
+
9
+ def is_administrator?
10
+ self.is_administrator
11
+ end
12
+
13
+ def is_anonymous?
14
+ false
15
+ end
16
+
17
+ def name
18
+ "#{first_name} #{last_name}"
19
+ end
20
+
21
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ConnectRails</title>
5
+ <%= stylesheet_link_tag "openstax/connect/application", :media => "all" %>
6
+ <%= javascript_include_tag "openstax/connect/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>