effective_learndash 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +126 -0
- data/Rakefile +18 -0
- data/app/assets/config/effective_learndash_manifest.js +3 -0
- data/app/assets/javascripts/effective_learndash/base.js +0 -0
- data/app/assets/javascripts/effective_learndash.js +1 -0
- data/app/assets/stylesheets/effective_learndash/base.scss +0 -0
- data/app/assets/stylesheets/effective_learndash.scss +1 -0
- data/app/controllers/admin/learndash_courses_controller.rb +15 -0
- data/app/controllers/admin/learndash_enrollments_controller.rb +9 -0
- data/app/controllers/admin/learndash_users_controller.rb +11 -0
- data/app/datatables/admin/effective_learndash_courses_datatable.rb +26 -0
- data/app/datatables/admin/effective_learndash_enrollments_datatable.rb +39 -0
- data/app/datatables/admin/effective_learndash_users_datatable.rb +25 -0
- data/app/helpers/effective_learndash_helper.rb +2 -0
- data/app/models/concerns/effective_learndash_owner.rb +64 -0
- data/app/models/effective/learndash_api.rb +269 -0
- data/app/models/effective/learndash_course.rb +39 -0
- data/app/models/effective/learndash_enrollment.rb +73 -0
- data/app/models/effective/learndash_user.rb +97 -0
- data/app/views/admin/learndash_courses/_learndash_course.html.haml +12 -0
- data/app/views/admin/learndash_enrollments/_form.html.haml +16 -0
- data/app/views/admin/learndash_owners/_form.html.haml +5 -0
- data/app/views/admin/learndash_users/_form.html.haml +6 -0
- data/app/views/admin/learndash_users/_learndash_user.html.haml +21 -0
- data/config/effective_learndash.rb +18 -0
- data/config/routes.rb +25 -0
- data/db/migrate/01_create_effective_learndash.rb.erb +48 -0
- data/db/seeds.rb +1 -0
- data/lib/effective_learndash/engine.rb +22 -0
- data/lib/effective_learndash/version.rb +3 -0
- data/lib/effective_learndash.rb +52 -0
- data/lib/generators/effective_learndash/install_generator.rb +29 -0
- data/lib/generators/templates/effective_learndash_mailer_preview.rb +4 -0
- data/lib/tasks/effective_learndash_tasks.rake +8 -0
- metadata +232 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Effective
|
2
|
+
class LearndashCourse < ActiveRecord::Base
|
3
|
+
has_many :learndash_enrollments
|
4
|
+
has_many :learndash_users, through: :learndash_enrollments
|
5
|
+
|
6
|
+
effective_resource do
|
7
|
+
# This user the wordpress credentials
|
8
|
+
course_id :integer
|
9
|
+
status :string
|
10
|
+
title :string
|
11
|
+
|
12
|
+
timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
scope :deep, -> { all }
|
16
|
+
scope :sorted, -> { order(:title) }
|
17
|
+
|
18
|
+
validates :course_id, presence: true
|
19
|
+
validates :status, presence: true
|
20
|
+
validates :title, presence: true
|
21
|
+
|
22
|
+
# Syncs all courses
|
23
|
+
def self.refresh!
|
24
|
+
courses = all()
|
25
|
+
|
26
|
+
EffectiveLearndash.api.courses.each do |data|
|
27
|
+
course = courses.find { |course| course.course_id == data[:id] } || new()
|
28
|
+
course.update!(course_id: data[:id], title: data.dig(:title, :rendered), status: data[:status], link: data[:link])
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
title.presence || 'learndash course'
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Effective
|
2
|
+
class LearndashEnrollment < ActiveRecord::Base
|
3
|
+
belongs_to :owner, polymorphic: true
|
4
|
+
log_changes(to: :owner) if respond_to?(:log_changes)
|
5
|
+
|
6
|
+
belongs_to :learndash_course
|
7
|
+
belongs_to :learndash_user
|
8
|
+
|
9
|
+
PROGRESS_STATUSES = ['not-started', 'in-progress', 'completed']
|
10
|
+
|
11
|
+
effective_resource do
|
12
|
+
last_synced_at :string
|
13
|
+
|
14
|
+
# Wordpress
|
15
|
+
progress_status :string
|
16
|
+
|
17
|
+
last_step :integer
|
18
|
+
steps_completed :integer
|
19
|
+
steps_total :integer
|
20
|
+
|
21
|
+
date_started :datetime
|
22
|
+
date_completed :datetime
|
23
|
+
|
24
|
+
timestamps
|
25
|
+
end
|
26
|
+
|
27
|
+
scope :completed, -> { where(progress_status: 'completed') }
|
28
|
+
scope :in_progress, -> { where(progress_status: 'in-progress') }
|
29
|
+
scope :not_started, -> { where(progress_status: 'not-started') }
|
30
|
+
|
31
|
+
scope :deep, -> { all }
|
32
|
+
scope :sorted, -> { order(:id) }
|
33
|
+
|
34
|
+
before_validation do
|
35
|
+
self.owner ||= learndash_user&.owner
|
36
|
+
end
|
37
|
+
|
38
|
+
validates :learndash_user_id, uniqueness: { scope: :learndash_course_id, message: 'already enrolled in this course' }
|
39
|
+
|
40
|
+
# First time sync only for creating a new enrollment from the form
|
41
|
+
validate(if: -> { last_synced_at.blank? && learndash_user.present? && learndash_course.present? && errors.blank? }) do
|
42
|
+
assign_api_attributes
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_s
|
46
|
+
persisted? ? "#{learndash_user} #{learndash_course}" : 'learndash enrollment'
|
47
|
+
end
|
48
|
+
|
49
|
+
def completed?
|
50
|
+
progress_status == 'completed'
|
51
|
+
end
|
52
|
+
|
53
|
+
def refresh!
|
54
|
+
assign_api_attributes
|
55
|
+
save!
|
56
|
+
end
|
57
|
+
|
58
|
+
def assign_api_attributes(data = nil)
|
59
|
+
data ||= EffectiveLearndash.api.find_enrollment(self) || EffectiveLearndash.api.create_enrollment(self)
|
60
|
+
|
61
|
+
assign_attributes(
|
62
|
+
last_synced_at: Time.zone.now,
|
63
|
+
progress_status: data[:progress_status],
|
64
|
+
last_step: data[:last_step],
|
65
|
+
steps_completed: data[:steps_completed],
|
66
|
+
steps_total: data[:steps_total],
|
67
|
+
date_started: Time.use_zone('UTC') { Time.zone.parse(data[:date_started]) },
|
68
|
+
date_completed: (Time.use_zone('UTC') { Time.zone.parse(data[:date_completed]) } if data[:date_completed].present?)
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Effective
|
2
|
+
class LearndashUser < ActiveRecord::Base
|
3
|
+
belongs_to :owner, polymorphic: true
|
4
|
+
log_changes(to: :owner) if respond_to?(:log_changes)
|
5
|
+
|
6
|
+
has_many :learndash_enrollments
|
7
|
+
accepts_nested_attributes_for :learndash_enrollments
|
8
|
+
|
9
|
+
has_many :learndash_courses, through: :learndash_enrollments
|
10
|
+
|
11
|
+
effective_resource do
|
12
|
+
last_synced_at :string
|
13
|
+
|
14
|
+
# This user the wordpress credentials
|
15
|
+
user_id :integer
|
16
|
+
email :string
|
17
|
+
username :string
|
18
|
+
password :string
|
19
|
+
|
20
|
+
timestamps
|
21
|
+
end
|
22
|
+
|
23
|
+
scope :deep, -> { includes(:owner) }
|
24
|
+
scope :sorted, -> { order(:id) }
|
25
|
+
|
26
|
+
# I want to validate owner uniqueness, and only then create the learndash user on wordpress
|
27
|
+
validate(if: -> { new_record? && owner.present? }) do
|
28
|
+
self.errors.add(:owner, "already exists") if self.class.where(owner: owner).exists?
|
29
|
+
end
|
30
|
+
|
31
|
+
# First time sync only for creating a new user from the form
|
32
|
+
validate(if: -> { last_synced_at.blank? && owner.present? && errors.blank? }) do
|
33
|
+
assign_api_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
with_options(if: -> { last_synced_at.present? }) do
|
37
|
+
validates :user_id, presence: true
|
38
|
+
validates :email, presence: true
|
39
|
+
validates :username, presence: true
|
40
|
+
validates :password, presence: true
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
owner&.to_s || username.presence || 'learndash user'
|
45
|
+
end
|
46
|
+
|
47
|
+
def refresh!
|
48
|
+
assign_api_course_enrollments
|
49
|
+
save!
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find
|
53
|
+
def enrollment(course:)
|
54
|
+
learndash_enrollments.find { |enrollment| enrollment.learndash_course_id == course.id }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Find or build
|
58
|
+
def build_enrollment(course:)
|
59
|
+
enrollment(course: course) || learndash_enrollments.build(learndash_course: course)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create
|
63
|
+
def create_enrollment(course:)
|
64
|
+
enrollment = build_enrollment(course: course)
|
65
|
+
enrollment.save!
|
66
|
+
enrollment
|
67
|
+
end
|
68
|
+
|
69
|
+
# Assign my model attributes from API. These should never change.
|
70
|
+
def assign_api_attributes(data = nil)
|
71
|
+
data ||= EffectiveLearndash.api.find_user(owner) || EffectiveLearndash.api.create_user(owner)
|
72
|
+
|
73
|
+
# Take special care not to overwrite password. We only get password once.
|
74
|
+
self.password ||= (data[:password].presence || 'unknown')
|
75
|
+
|
76
|
+
assign_attributes(email: data[:email], user_id: data[:id], username: data[:username], last_synced_at: Time.zone.now)
|
77
|
+
end
|
78
|
+
|
79
|
+
# This synchronizes all the course enrollments from the API down locally.
|
80
|
+
def assign_api_course_enrollments
|
81
|
+
raise('must be persisted') unless persisted?
|
82
|
+
|
83
|
+
courses = LearndashCourse.all()
|
84
|
+
|
85
|
+
EffectiveLearndash.api.user_enrollments(self).each do |data|
|
86
|
+
course = courses.find { |course| course.course_id == data[:course] }
|
87
|
+
raise("unable to find local persisted learndash course for id #{data[:course]}. Run Effective::LearndashCourse.sync!") unless course.present?
|
88
|
+
|
89
|
+
enrollment = build_enrollment(course: course)
|
90
|
+
enrollment.assign_api_attributes(data)
|
91
|
+
end
|
92
|
+
|
93
|
+
assign_attributes(last_synced_at: Time.zone.now)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= card('Learndash Course') do
|
2
|
+
%p= learndash_course.title
|
3
|
+
%p= link_to(learndash_course.link, learndash_course.link, target: '_blank')
|
4
|
+
|
5
|
+
%p= link_to "Learndash LMS Course Admin", EffectiveLearndash.learndash_url.chomp('/') + "/wp-admin/post.php?post=#{learndash_course.course_id}}&action=edit", target: '_blank'
|
6
|
+
|
7
|
+
%h3 Learndash User Enrollments
|
8
|
+
|
9
|
+
%p Click the New button from the below table to enroll a user into this course.
|
10
|
+
|
11
|
+
- datatable = Admin::EffectiveLearndashEnrollmentsDatatable.new(learndash_course: learndash_course)
|
12
|
+
= render_datatable(datatable, inline: true)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
= effective_form_with(model: [:admin, learndash_enrollment], engine: true) do |f|
|
2
|
+
= f.hidden_field :learndash_course_id
|
3
|
+
= f.hidden_field :learndash_user_id
|
4
|
+
|
5
|
+
- if inline_datatable?
|
6
|
+
- if inline_datatable.attributes[:learndash_course_id].blank?
|
7
|
+
- collection = Effective::LearndashCourse.all.deep.sorted.where.not(id: f.object.learndash_user&.learndash_courses)
|
8
|
+
= f.select :learndash_course_id, collection
|
9
|
+
|
10
|
+
- if inline_datatable.attributes[:learndash_user_id].blank?
|
11
|
+
= f.select :learndash_user_id, Effective::LearndashUser.all.deep.sorted
|
12
|
+
- else
|
13
|
+
= f.select :learndash_course_id, Effective::LearndashCourse.all.deep.sorted
|
14
|
+
= f.select :learndash_user_id, Effective::LearndashUser.all.deep.sorted
|
15
|
+
|
16
|
+
= f.submit
|
@@ -0,0 +1,21 @@
|
|
1
|
+
= card('Learndash User') do
|
2
|
+
%p
|
3
|
+
= link_to(learndash_user.owner, "/admin/users/#{learndash_user.owner.to_param}/edit")
|
4
|
+
has an existing account on Learndash.
|
5
|
+
|
6
|
+
%ul
|
7
|
+
%li Email: #{learndash_user.email}
|
8
|
+
%li Username: #{learndash_user.username}
|
9
|
+
%li Password: #{learndash_user.password}
|
10
|
+
|
11
|
+
%p= link_to "Learndash LMS User Admin", EffectiveLearndash.learndash_url.chomp('/') + "/wp-admin/user-edit.php?user_id=#{learndash_user.user_id}", target: '_blank'
|
12
|
+
|
13
|
+
%p
|
14
|
+
%small Last refreshed #{time_ago_in_words(learndash_user.last_synced_at)} ago.
|
15
|
+
|
16
|
+
%h3 Learndash Course Enrollments
|
17
|
+
|
18
|
+
%p Click the New button from the below table to manually enroll this user into a new course.
|
19
|
+
|
20
|
+
- datatable = Admin::EffectiveLearndashEnrollmentsDatatable.new(learndash_user: learndash_user)
|
21
|
+
= render_datatable(datatable, inline: true)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
EffectiveLearndash.setup do |config|
|
2
|
+
# Layout Settings
|
3
|
+
# Configure the Layout per controller, or all at once
|
4
|
+
# config.layout = { application: 'application', admin: 'admin' }
|
5
|
+
|
6
|
+
# Application Password
|
7
|
+
# This is an application password from your Wordpress install hosting Learndash plugin.
|
8
|
+
# https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/
|
9
|
+
#
|
10
|
+
config.learndash_url = ENV['LEARNDASH_URL']
|
11
|
+
config.learndash_username = ENV['LEARNDASH_USERNAME']
|
12
|
+
config.learndash_password = ENV['LEARNDASH_PASSWORD']
|
13
|
+
|
14
|
+
# Customize the method used to assign Wordpress username and passwords
|
15
|
+
# Usernames can only contain lowercase letters (a-z) and numbers.
|
16
|
+
# config.wp_username = Proc.new { |user| "user#{user.id}" }
|
17
|
+
# config.wp_password = Proc.new { |user| SecureRandom.base64(12) }
|
18
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Rails.application.routes.draw do
|
4
|
+
mount EffectiveLearndash::Engine => '/', as: 'effective_learndash'
|
5
|
+
end
|
6
|
+
|
7
|
+
EffectiveLearndash::Engine.routes.draw do
|
8
|
+
namespace :admin do
|
9
|
+
get '/learndash', to: 'learndash#index', as: :learndash
|
10
|
+
|
11
|
+
resources :learndash_users, only: [:index, :show, :new, :create, :update] do
|
12
|
+
post :refresh, on: :member
|
13
|
+
end
|
14
|
+
|
15
|
+
resources :learndash_enrollments, only: [:index, :new, :create, :update] do
|
16
|
+
post :refresh, on: :member
|
17
|
+
end
|
18
|
+
|
19
|
+
resources :learndash_courses, only: [:index, :show, :update] do
|
20
|
+
get :refresh, on: :collection
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class CreateEffectiveLearndash < ActiveRecord::Migration[6.1]
|
2
|
+
create_table :learndash_users do |t|
|
3
|
+
t.integer :owner_id
|
4
|
+
t.string :owner_type
|
5
|
+
|
6
|
+
t.datetime :last_synced_at
|
7
|
+
|
8
|
+
# Wordpress
|
9
|
+
t.integer :user_id
|
10
|
+
t.string :email
|
11
|
+
t.string :username
|
12
|
+
t.string :password
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :learndash_courses do |t|
|
18
|
+
# Wordpress
|
19
|
+
t.integer :course_id
|
20
|
+
t.string :status
|
21
|
+
t.string :title
|
22
|
+
t.string :link
|
23
|
+
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
|
27
|
+
create_table :learndash_enrollments do |t|
|
28
|
+
t.integer :owner_id
|
29
|
+
t.string :owner_type
|
30
|
+
|
31
|
+
t.integer :learndash_course_id
|
32
|
+
t.integer :learndash_user_id
|
33
|
+
|
34
|
+
t.datetime :last_synced_at
|
35
|
+
|
36
|
+
# Wordpress
|
37
|
+
t.string :progress_status
|
38
|
+
|
39
|
+
t.integer :last_step
|
40
|
+
t.integer :steps_completed
|
41
|
+
t.integer :steps_total
|
42
|
+
|
43
|
+
t.datetime :date_started
|
44
|
+
t.datetime :date_completed
|
45
|
+
|
46
|
+
t.timestamps
|
47
|
+
end
|
48
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts "Running effective_learndash seeds"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EffectiveLearndash
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
engine_name 'effective_learndash'
|
4
|
+
|
5
|
+
# Set up our default configuration options.
|
6
|
+
initializer 'effective_learndash.defaults', before: :load_config_initializers do |app|
|
7
|
+
eval File.read("#{config.root}/config/effective_learndash.rb")
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer 'effective_learndash.assets' do |app|
|
11
|
+
app.config.assets.precompile += ['effective_learndash_manifest.js', 'effective_learndash/*']
|
12
|
+
end
|
13
|
+
|
14
|
+
# Include acts_as_addressable concern and allow any ActiveRecord object to call it
|
15
|
+
initializer 'effective_learndash.active_record' do |app|
|
16
|
+
ActiveSupport.on_load :active_record do
|
17
|
+
ActiveRecord::Base.extend(EffectiveLearndashOwner::Base)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'effective_resources'
|
2
|
+
require 'effective_datatables'
|
3
|
+
require 'effective_learndash/engine'
|
4
|
+
require 'effective_learndash/version'
|
5
|
+
|
6
|
+
module EffectiveLearndash
|
7
|
+
WP_USERNAME_PROC = Proc.new { |user| "user#{user.id}" }
|
8
|
+
WP_PASSWORD_PROC = Proc.new { |user| SecureRandom.base64(12) }
|
9
|
+
|
10
|
+
def self.config_keys
|
11
|
+
[
|
12
|
+
:learndash_url, :learndash_username, :learndash_password,
|
13
|
+
:wp_username, :wp_password,
|
14
|
+
:layout
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
include EffectiveGem
|
19
|
+
|
20
|
+
def self.api
|
21
|
+
raise('please set learndash_url in config/initializers/effective_learndash.rb') unless learndash_url.present?
|
22
|
+
raise('please set learndash_username in config/initializers/effective_learndash.rb') unless learndash_username.present?
|
23
|
+
raise('please set learndash_password in config/initializers/effective_learndash.rb') unless learndash_password.present?
|
24
|
+
|
25
|
+
Effective::LearndashApi.new(
|
26
|
+
url: learndash_url,
|
27
|
+
username: learndash_username,
|
28
|
+
password: learndash_password
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.wp_username
|
33
|
+
config[:wp_username] || WP_USERNAME_PROC
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.wp_password
|
37
|
+
config[:wp_password] || WP_PASSWORD_PROC
|
38
|
+
end
|
39
|
+
|
40
|
+
# The user.learndash_username is the source of truth
|
41
|
+
# This is the backup to generate a new username
|
42
|
+
def self.wp_username_for(owner)
|
43
|
+
raise('expecting a learndash owner') unless owner.class.respond_to?(:effective_learndash_owner?)
|
44
|
+
owner.instance_exec(owner, &wp_username)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.wp_password_for(owner)
|
48
|
+
raise('expecting a learndash owner') unless owner.class.respond_to?(:effective_learndash_owner?)
|
49
|
+
owner.instance_exec(owner, &wp_password)
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module EffectiveLearndash
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
desc 'Creates an EffectiveLearndash initializer in your application.'
|
7
|
+
|
8
|
+
source_root File.expand_path('../../templates', __FILE__)
|
9
|
+
|
10
|
+
def self.next_migration_number(dirname)
|
11
|
+
if not ActiveRecord::Base.timestamped_migrations
|
12
|
+
Time.new.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
else
|
14
|
+
'%.3d' % (current_migration_number(dirname) + 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_initializer
|
19
|
+
template ('../' * 3) + 'config/effective_learndash.rb', 'config/initializers/effective_learndash.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_migration_file
|
23
|
+
@learndash_courses_table_name = ':' + EffectiveLearndash.learndash_courses_table_name.to_s
|
24
|
+
migration_template ('../' * 3) + 'db/migrate/01_create_effective_learndash.rb.erb', 'db/migrate/create_effective_learndash.rb'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|