Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul target editor #176

Merged
merged 174 commits into from Feb 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
174 commits
Select commit Hold shift + click to select a range
5d3ce69
Convert school curriculum components to JSX3
harigopal Jan 13, 2020
cf3627e
Set up a routed drawer for new components
harigopal Jan 13, 2020
faee4a3
Add buttons on curriculum editor to switch between editors
harigopal Jan 14, 2020
fcb6a0d
Allow the EditorDrawer component to accept size
harigopal Jan 14, 2020
ed04222
Start loading target content in new editor
harigopal Jan 14, 2020
115ba7e
Begin working on a new content block editor
harigopal Jan 14, 2020
1542970
Create graph query to fetch target details
Jan 14, 2020
3f3aacc
Create target details component and load target data
Jan 14, 2020
fb30445
Send target to the content editor instead of targetId
harigopal Jan 15, 2020
43923a3
Add a tabbing mechanism inside the new target editor drawer
harigopal Jan 15, 2020
d670fef
Replace step number with icon in target editor drawer tabs
harigopal Jan 15, 2020
ae20ae1
Set target title as heading in the drawer
harigopal Jan 15, 2020
2bfcb6a
Add quiz data to target details query
Jan 16, 2020
8f4bec2
Revert changes to old types for quiz
Jan 16, 2020
c301e11
Add title and prerequisites to target details editor
Jan 16, 2020
785dcc9
Merge branch 'master' into overhaul-target-editor
Jan 16, 2020
c6d8524
Fix issue with setting state for prerequisite target ids
Jan 16, 2020
c493a9b
Implement evaluation criteria selector in target editor
Jan 16, 2020
8f86568
WIP: Add link to complete field for target editor
Jan 16, 2020
3c589c1
TargetDrawer: Fix crash on opening editor for new target
Jan 17, 2020
3a8390a
Add method of completion selector in target details editor
Jan 17, 2020
d72435a
Create new mutation for creating markdown content block
harigopal Jan 17, 2020
9b86b7a
Add quiz to target details editor
Jan 17, 2020
c7227f3
Avoid N+1 queries in target details resolver
Jan 17, 2020
dc90db6
Create full flow for adding new markdown content block
harigopal Jan 17, 2020
057a0b6
Begin working on the file upload buttons
harigopal Jan 20, 2020
86c19bb
Merge branch 'master' into overhaul-target-editor
Jan 20, 2020
fd1689c
Add visibility field to target editor
Jan 21, 2020
1a64eab
WIP: Create mutation to update targets [skip ci]
Jan 21, 2020
1d4ff78
Get both file-based buttons working on content block creator
harigopal Jan 21, 2020
01c774a
Fix issue with quiz input type in mutation
Jan 21, 2020
7e24e78
Create mutation to update target details
Jan 22, 2020
8122a56
Refactor types and elements in target details editor
Jan 22, 2020
dfbe4a5
Add disabling cover to target details editor
Jan 22, 2020
fcbebcb
Fix crash while updating target quiz
Jan 22, 2020
6be0bb1
Add a concern to authorize course authors
Jan 22, 2020
edf2eeb
Update target visibility in component state
Jan 22, 2020
9ba0bc0
Merge branch 'master' into overhaul-target-editor
Jan 23, 2020
260d331
WIP: Add versions editor
bodhish Jan 23, 2020
d5e8e9d
Create the UI for the embed form
harigopal Jan 23, 2020
e67c19f
Add content versions editor
bodhish Jan 23, 2020
1b84d29
Improve label styling for target editor form
Jan 23, 2020
29afcf4
Enable verion selection
bodhish Jan 23, 2020
02d6525
Upgrade Rubocop to latest
harigopal Jan 23, 2020
354c7e7
Get the embed form working
harigopal Jan 23, 2020
8178b0e
Allow the creator at the bottom of the UI to also close the embed form
harigopal Jan 23, 2020
18e94db
Add version_at to content versions
bodhish Jan 23, 2020
fa029d4
Fix margin for target editor input fields
Jan 23, 2020
07a3a6a
Clean up the code for top bar in block creator
harigopal Jan 23, 2020
2aad6a3
Simplify target type for curriculum index
Jan 24, 2020
3bd9a64
Avoid preloading data for targets in curriculum index
Jan 24, 2020
227d708
Scope targets by school in update target mutator
Jan 24, 2020
57c6a4f
Get the delete block button working
harigopal Jan 24, 2020
52fddd3
Update target basic info updates to parent component
Jan 24, 2020
6d1bf4c
Get the sort buttons working
harigopal Jan 25, 2020
06b1afa
Rename components for handling quiz in target editor
Jan 27, 2020
7bb6ca4
Wire the dirty state, and the save / undo buttons together
harigopal Jan 27, 2020
c34cfb9
Demo update flow working with file block
harigopal Jan 28, 2020
0652f53
Add target versions
bodhish Jan 28, 2020
24d7a18
Add create target version service
bodhish Jan 28, 2020
3713302
Store a dirty flag in drawer, use it, and allow children to set it
harigopal Jan 28, 2020
0e91987
Expose target relationship with content versions
bodhish Jan 28, 2020
1e9171d
Set target_version relationship to optional temporarily
harigopal Jan 28, 2020
0e0f195
Ensure elements inside the a block editor have correct height
harigopal Jan 28, 2020
ed210a5
Set up a basic v2 for markdown editor
harigopal Jan 28, 2020
611d5f0
Implement a rudimentary full-screen MD editor
harigopal Jan 28, 2020
06edd0a
WIP: Create a generic multiselect inline component [skip ci]
Jan 28, 2020
f25c0a2
Correctly handle sizing of MD editor textarea in different modes
harigopal Jan 28, 2020
2058856
Add image and embed blocks in the new editor
harigopal Jan 29, 2020
8c88caf
Improve styling of selected items in dropdown
Jan 29, 2020
c4e437f
Improve texts in the multiselect inline component
Jan 29, 2020
46d80f6
Set up framwork necessary for advanced features in v2 editor [skip ci]
harigopal Jan 29, 2020
79668d6
Add basic markdown helper buttons
harigopal Jan 29, 2020
6a7b4bc
Start with selection at the end of the markdown text
harigopal Jan 29, 2020
bf4df98
Press ESC key to exit full-screen mode
harigopal Jan 29, 2020
10f81fb
Use multiselect dropdown for setting evaluation criteria
Jan 29, 2020
d47da86
Create custom keyboard event handling for the bold and italics controls
harigopal Jan 29, 2020
c23d1c1
Get the file uploader working
harigopal Jan 29, 2020
4be4cd0
Allow multiselect inline component to accept color for selected
Jan 30, 2020
d0940ff
Improve select icon for multiselect dropdown list
Jan 30, 2020
40b4b72
Allow preview tab to scroll properly in fullscreen mode
harigopal Jan 30, 2020
c103965
Do not show editor footer or text controls in preview mode [skip ci]
harigopal Jan 30, 2020
4a5b531
Center and limit width of preview in FS mode [skip ci]
harigopal Jan 30, 2020
88ded6a
Set dirty in editor drawer when target details are changed
Jan 30, 2020
abf0d0b
Spec the new target content editor
harigopal Jan 31, 2020
81111ab
Update target details resolver
bodhish Feb 2, 2020
8419b9d
Sync the scroll height of preview with the markdown source
harigopal Feb 2, 2020
c49c93e
Add a new multiselect-inline package
Feb 2, 2020
b38b273
Merge branch 'master' into overhaul-target-editor
Feb 2, 2020
128f1c9
Use multiselect inline component from packages
Feb 2, 2020
999aa54
Update correct state while selecting evaluation criterion
Feb 2, 2020
3ed251c
Fix incorrect table name in migration
harigopal Feb 3, 2020
ea68ced
Bump the selection range after changing textarea value [skip ci]
harigopal Feb 3, 2020
b681b98
Improve spacing on the editor in FS mode [skip ci]
harigopal Feb 3, 2020
869a14e
Add spec for target details editor
Feb 3, 2020
18d8115
Rename version_on to version_at
bodhish Feb 3, 2020
9899179
Create target version on creating a new target
Feb 3, 2020
75b5e9c
Cleanup and simplify curriculum editor spec
Feb 3, 2020
989689c
Minor style fix on multiselect inline component
vinutv Feb 3, 2020
2ccd155
Merge branch 'overhaul-target-editor' of https://github.com/SVdotCO/p…
vinutv Feb 3, 2020
de3a264
Fix a bug create target version migration
bodhish Feb 3, 2020
3c64cfa
Copy files along with content blocks
bodhish Feb 3, 2020
df1d5a1
Add create target version mutator
bodhish Feb 3, 2020
06fb251
Handle target versions in course clone service
Feb 3, 2020
a727067
Create factory for target versions
Feb 3, 2020
2d0ea5b
Update create target version migration
bodhish Feb 3, 2020
74a57e7
Fix few failing specs
Feb 3, 2020
8becc53
Replace version_at with version id
bodhish Feb 4, 2020
88cad22
Remove version_at form target versions
bodhish Feb 4, 2020
5b005a6
Fix target status computation for teams #189
harigopal Feb 3, 2020
7c62a28
Hide current password field if it isn't required #184
harigopal Feb 3, 2020
0731bca
Do case-insensitive sort for teams listed in coach's students interface
harigopal Feb 4, 2020
ea7e698
Create an example page for multiselect inline package
Feb 4, 2020
ae01e42
Add tailwind to package css
Feb 4, 2020
ef8fc51
Reset search text in on selecting an item
Feb 4, 2020
fdefff1
Add updated at to target version
bodhish Feb 4, 2020
0657ae3
Merge branch 'overhaul-target-editor' of github.com:SVdotCO/pupilfirs…
bodhish Feb 4, 2020
1d44674
Wire create target version mutation
bodhish Feb 4, 2020
429f6ea
Cleanup components for the old target editor
Feb 5, 2020
451eef5
Add validations for create target version mutator
bodhish Feb 5, 2020
db0c2db
Fix curriculum editor spec
Feb 5, 2020
2019741
Fix a bug in target version mutator
bodhish Feb 5, 2020
1f1037a
Use unified target content view in admin and students
bodhish Feb 5, 2020
8bf655c
Cleanup unused target update route
Feb 5, 2020
21646d0
Add spec for target version management
bodhish Feb 5, 2020
c33f5c8
Update documentation for multiselect inline pack
Feb 5, 2020
f5a71e9
Publish
Feb 5, 2020
adcbd67
WIP: Wire target version with content block mutators
bodhish Feb 6, 2020
6ba35e7
Fix image and file upload in content editor
bodhish Feb 6, 2020
d79c291
Wire target version with all content block operations
bodhish Feb 6, 2020
ee29224
Update content editor spec
bodhish Feb 6, 2020
4d6564b
Update target version spec
bodhish Feb 6, 2020
3e88922
Fix a bug in curriculum editor spec
bodhish Feb 6, 2020
eb699a2
Fix partial application of finalizeChange function
harigopal Feb 6, 2020
37a46a7
Seed content blocks properly with a target version
harigopal Feb 6, 2020
0c46579
Only report dirty content block status when absolutely required
harigopal Feb 6, 2020
9ab8849
Improve styling of borders in new markdown editor
harigopal Feb 7, 2020
948d885
Avoid focus-outline on some buttons and reduce use of FaIcon component
harigopal Feb 7, 2020
89748c3
Make quick links on curriculum editor target rows larger
harigopal Feb 7, 2020
bf68e2e
Improve wording of text related to target content versioning
harigopal Feb 7, 2020
757a272
Fix issue with save button not appearing after a change is saved
harigopal Feb 7, 2020
b7e6e7c
Upgrade all JSX3 usage of v1 Markdown editor to v2
harigopal Feb 7, 2020
d2a4e0e
Use padding instead of margin for vertical spacing in most markdown-r…
harigopal Feb 7, 2020
57fd385
Add the stylelint package as a dev dependency
harigopal Feb 8, 2020
0a3210b
Remove trailing semicolon from v2 Markdown editor's CSS
harigopal Feb 8, 2020
e97d9dc
Set up overcommit to run stylelint pre-commit
harigopal Feb 8, 2020
8230a72
Refactor class names for multiselect-inline component
Feb 10, 2020
e92ba65
Publish
Feb 10, 2020
682eba3
Update graphql schema
bodhish Feb 10, 2020
acc8d95
Remove sort content block mutator
bodhish Feb 10, 2020
c3d8f83
Remove latest_version from content block
bodhish Feb 10, 2020
e86f2dd
Remove some unused methods and a route
harigopal Feb 10, 2020
632d7fb
Remove unused services related to content version
bodhish Feb 10, 2020
bcd0d47
Remove update sort index service
bodhish Feb 10, 2020
0486674
MultiselectInline: Add a message when all items are selected
Feb 10, 2020
20190ad
Publish
Feb 10, 2020
1d49594
Add custom message to multiselect inline documentation
Feb 10, 2020
ef71170
Publish
Feb 10, 2020
883aef1
Replace GraphqlQuery.sendQuery with sendQuery2 and remove the latter
harigopal Feb 10, 2020
ec6c342
Remove invalid semicolon from CSS file
harigopal Feb 10, 2020
3edf55a
Authorize directly against Schools::LevelPolicy in ContentBlocksContr…
harigopal Feb 10, 2020
b1bcc44
Minor update to multiselect-inline package documentation
Feb 10, 2020
cca1cb0
Merge branch 'master' into overhaul-target-editor
harigopal Feb 10, 2020
790a052
Add few validations in update target mutator
Feb 10, 2020
f7dd755
Cleanup unused update target form and service
Feb 10, 2020
1c516af
Refactor couple of function names in TargetDetails type
Feb 10, 2020
76ae3f4
Bring back the target update service
Feb 10, 2020
a83afdc
Refactor css classnames for inline dropdown component
Feb 11, 2020
80da435
Add url validation for target#link_to_complete
Feb 11, 2020
bfdd188
UpdateTargetMutator: Link to complete could be blank
Feb 11, 2020
3b9bf52
Remove target_id from target version mutator
bodhish Feb 11, 2020
4bf9972
Clean up target versions editor
bodhish Feb 11, 2020
02abcf8
TargetShowSpec: All targets should have content
Feb 11, 2020
ee37f02
Enable curriculum preview mode for course authors
bodhish Feb 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .overcommit.yml
Expand Up @@ -8,3 +8,8 @@ PreCommit:
exclude:
- '**/*.svg'
- 'app/javascript/**/*.bs.js'

Stylelint:
enabled: true
command: ['yarn', 'run', '--silent', 'stylelint']
required_executable: './node_modules/stylelint/bin/stylelint.js'
18 changes: 10 additions & 8 deletions .rubocop.yml
Expand Up @@ -27,9 +27,9 @@ Style/FormatStringToken:
Enabled: false
Style/DoubleNegation:
Enabled: false
Layout/AlignParameters:
Layout/ParameterAlignment:
EnforcedStyle: with_fixed_indentation
Layout/AlignArguments:
Layout/ArgumentAlignment:
EnforcedStyle: with_fixed_indentation
Layout/MultilineOperationIndentation:
EnforcedStyle: indented
Expand All @@ -40,6 +40,8 @@ Layout/CaseIndentation:
IndentOneStep: true
Layout/EndAlignment:
EnforcedStyleAlignWith: variable
Layout/LineLength:
Enabled: false
Naming/VariableNumber:
Enabled: false
Metrics/BlockLength:
Expand All @@ -48,8 +50,6 @@ Metrics/BlockLength:
- 'spec/**/*_spec.rb'
- 'config/**/*'
- 'spec/factories/*'
Metrics/LineLength:
Enabled: false
Lint/ShadowingOuterLocalVariable: # shadowing is a language feature - we should use it where appropriate.
Enabled: false

Expand All @@ -61,10 +61,6 @@ Bundler/OrderedGems:
Enabled: false # we have a ton of gems, grouped arbitrarily. Could be tackled later.
Style/FrozenStringLiteralComment:
Enabled: false # forces addition of frozen string literal directive to all files. WTF.
StringLiterals:
Enabled: false
Documentation:
Enabled: false
Metrics/MethodLength:
Max: 20 # default was 10.
Metrics/ClassLength:
Expand All @@ -75,3 +71,9 @@ Style/GuardClause:
Enabled: false # this thing is annoying, and can lead to not-as-readable code.
Style/YodaCondition:
Enabled: false # buggy - sometimes complains about fixed code, and autocorrect doesn't work.
Style/StringLiterals:
Enabled: false
Style/Documentation:
Enabled: false
Layout/RescueEnsureAlignment:
Enabled: false # buggy
19 changes: 19 additions & 0 deletions .stylelintrc.json
@@ -0,0 +1,19 @@
{
"extends": "stylelint-config-recommended",
"rules": {
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"tailwind",
"apply",
"variants",
"responsive",
"screen"
]
}
],
"declaration-block-trailing-semicolon": null,
"no-descending-specificity": null
}
}
4 changes: 2 additions & 2 deletions Gemfile
Expand Up @@ -155,8 +155,8 @@ group :development, :test do
gem 'coderay', '~> 1.1' # Pretty syntax highlighting on rspec failure snippets.
gem 'pry-rails', '~> 0.3.5' # Pry debugger.
gem 'webmock', '~> 3.5' # Mocking web requests.
gem 'rubocop', '~> 0.74', require: false # Ruby Style Guide.
gem 'rubocop-rails', '~> 2.3', require: false # A RuboCop extension focused on enforcing Rails best practices and coding conventions.
gem 'rubocop', '~> 0.79', require: false # Ruby Style Guide.
gem 'rubocop-rails', '~> 2.4', require: false # A RuboCop extension focused on enforcing Rails best practices and coding conventions.
gem 'bundler-audit', '~> 0.5', require: false # Audit gems in gemfile.lock for reported vulnerabilities
gem 'overcommit', '~> 0.38', require: false # A fully configurable and extendable Git hook manager
gem 'fuubar', '~> 2.5' # The instafailing RSpec progress bar formatter.
Expand Down
20 changes: 10 additions & 10 deletions Gemfile.lock
Expand Up @@ -392,7 +392,7 @@ GEM
iniparse (1.4.4)
insensitive_hash (0.3.3)
intercom (3.8.0)
jaro_winkler (1.5.3)
jaro_winkler (1.5.4)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
jmespath (1.4.0)
Expand Down Expand Up @@ -495,8 +495,8 @@ GEM
overcommit (0.49.1)
childprocess (>= 0.6.3, < 2.0)
iniparse (~> 1.4)
parallel (1.17.0)
parser (2.6.4.1)
parallel (1.19.1)
parser (2.7.0.2)
ast (~> 2.4.0)
pg (1.1.4)
polyamorous (2.3.0)
Expand All @@ -519,7 +519,7 @@ GEM
nio4r (~> 2.0)
pundit (2.1.0)
activesupport (>= 3.0.0)
rack (2.0.8)
rack (2.1.1)
rack-contrib (2.1.0)
rack (~> 2.0)
rack-cors (1.0.6)
Expand Down Expand Up @@ -675,14 +675,14 @@ GEM
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-support (3.9.0)
rubocop (0.74.0)
rubocop (0.79.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
parser (>= 2.7.0.1)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-rails (2.3.2)
rubocop-rails (2.4.1)
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-progressbar (1.10.1)
Expand Down Expand Up @@ -758,7 +758,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
unicode-display_width (1.6.1)
uniform_notifier (1.12.1)
url_mount (0.2.1)
rack
Expand Down Expand Up @@ -903,8 +903,8 @@ DEPENDENCIES
rspec-eventually (~> 0.2.2)
rspec-rails (~> 4.0.0.beta3)
rspec-retry (~> 0.5)
rubocop (~> 0.74)
rubocop-rails (~> 2.3)
rubocop (~> 0.79)
rubocop-rails (~> 2.4)
sass-rails (>= 6)
scarf (~> 0.2)
scout_apm (~> 2.6)
Expand Down
33 changes: 9 additions & 24 deletions app/controllers/schools/content_blocks_controller.rb
@@ -1,37 +1,22 @@
module Schools
class ContentBlocksController < SchoolsController
before_action :authorize_target
include CamelizeKeys
include StringifyIds

# POST /school/targets/:target_id/content_block
def create
target = Target.find(params[:target_id])

# Let's authorize against the level, since that's the resource we have with a matching action in the policy.
authorize(target.level, policy_class: Schools::LevelPolicy)

form = ::Schools::Targets::CreateContentBlockForm.new(ContentBlock.new)

if form.validate(content_block_params)
content_block = form.save
render json: content_block_data(content_block)
if form.validate(params)
render json: camelize_keys(stringify_ids(form.save.merge(error: nil)))
else
render json: { error: form.errors.full_messages.join(', ') }
end
end

protected

def authorize_target
authorize(Target.find(params[:target_id]), policy_class: Schools::TargetPolicy)
end

def content_block_data(content_block)
content_block_data = { id: content_block.id.to_s, content: content_block.content, error: nil }
content_block.file.attached? ? content_block_data.merge!(fileUrl: url_for(content_block.file)) : content_block_data
content_block_data.merge(versions: target_versions)
end

def content_block_params
params[:content_block].merge(target_id: params[:target_id], content_sort_indices: JSON.parse(params[:content_sort_indices]))
end

def target_versions
Target.find(params[:target_id]).content_versions.order('version_on DESC').distinct(:version_on).pluck(:version_on)
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/schools/courses_controller.rb
Expand Up @@ -21,7 +21,7 @@ def attach_images

# GET /courses/:id/curriculum
def curriculum
course = scope.where(id: params[:id]).includes([:evaluation_criteria, :levels, :target_groups, targets: [:evaluation_criteria, :prerequisite_targets, :resources, quiz: { quiz_questions: %I[answer_options correct_answer] }]]).first
course = scope.where(id: params[:id]).includes(:evaluation_criteria, :levels, :target_groups, :targets).first
@course = authorize(course, policy_class: Schools::CoursePolicy)
end

Expand Down
28 changes: 9 additions & 19 deletions app/controllers/schools/targets_controller.rb
@@ -1,28 +1,18 @@
module Schools
class TargetsController < SchoolsController
before_action :load_target, only: %w[update]
layout 'school'

# PATCH /school/targets/:id
def update
form = ::Schools::Targets::UpdateForm.new(@target)

if form.validate(params[:target])
form.save
render json: { error: nil }
else
render json: { error: form.errors.full_messages.join(', ') }
end
end

# GET /school/targets/:id/content
# GET /school/courses/:course_id/targets/:id/content
def content
render json: camelize_keys(stringify_ids(Targets::FetchContentService.new(@target).details))
@course = current_school.courses.find(params[:course_id])
authorize(@course.targets.find(params[:id]), policy_class: Schools::TargetPolicy)
render 'schools/courses/curriculum'
end

protected
# GET /school/courses/:course_id/targets/:id/details
alias details content

def load_target
@target = authorize(Target.find(params[:id]), policy_class: Schools::TargetPolicy)
end
# GET /school/courses/:course_id/targets/:id/versions
alias versions content
end
end
2 changes: 1 addition & 1 deletion app/controllers/users/omniauth_callbacks_controller.rb
Expand Up @@ -10,7 +10,7 @@ def oauth_callback
if oauth_origin.present?
if @email.blank?
redirect_to oauth_error_url(host: oauth_origin[:fqdn], error: email_blank_flash)
return
nil
else
sign_in_at_oauth_origin
end
Expand Down
92 changes: 69 additions & 23 deletions app/forms/schools/targets/create_content_block_form.rb
@@ -1,30 +1,54 @@
module Schools
module Targets
class CreateContentBlockForm < Reform::Form
property :block_type, validates: { presence: true, inclusion: { in: ContentBlock.valid_block_types } }
property :block_type, validates: { presence: true, inclusion: { in: %w[image file] } }
property :target_id, virtual: true, validates: { presence: true }
property :url, virtual: true
property :content_sort_indices, virtual: true, validates: { presence: true }
property :file, virtual: true
property :markdown, virtual: true
property :title, virtual: true
property :caption, virtual: true
property :above_content_block_id, virtual: true

validates :file, presence: true, image: true, file_size: { less_than: 5.megabytes }, if: :image_block?
validates :file, presence: true, file_size: { less_than: 10.megabytes }, if: :file_block?
validates :markdown, presence: true, if: :markdown_block?
validates :url, presence: true, if: :embed_block?

def save
::ContentBlocks::CreateService.new(target, content_block_params).execute
ContentBlock.transaction do
content_block = create_file_or_image_block
shift_content_blocks_below(content_block)
json_attributes(content_block)
end
end

private

def create_file_or_image_block
target_version.content_blocks.create!(
block_type: block_type,
content: content(block_type),
file: file,
sort_index: sort_index
)
end

def content(block_type)
filename = file.original_filename

case block_type
when 'image'
{ caption: filename }
when 'file'
{ title: filename }
else
raise "Unexpected block type #{block_type} encountered when creating file-based content block for target with ID #{target_id}"
end
end

def target
Target.find(target_id)
end

def target_version
@target_version ||= target.current_target_version
end

def image_block?
block_type == ContentBlock::BLOCK_TYPE_IMAGE
end
Expand All @@ -33,24 +57,46 @@ def file_block?
block_type == ContentBlock::BLOCK_TYPE_FILE
end

def markdown_block?
block_type == ContentBlock::BLOCK_TYPE_MARKDOWN
def above_content_block
@above_content_block ||= begin
target.content_blocks.find_by(id: above_content_block_id) if above_content_block_id.present?
end
end

def embed_block?
block_type == ContentBlock::BLOCK_TYPE_EMBED
def sort_index
@sort_index ||= begin
if above_content_block.present?
# Put at the same position as 'above_content_block'.
above_content_block.sort_index
else
# Put at the bottom.
content_blocks.maximum(:sort_index) + 1
end
end
end

def content_block_params
{
block_type: block_type,
url: url,
content_sort_indices: content_sort_indices,
file: file,
markdown: markdown,
title: title,
caption: caption
}
def content_blocks
target_version.content_blocks
end

def shift_content_blocks_below(content_block)
content_blocks.where.not(id: content_block.id).where('sort_index >= ?', sort_index)
.update_all('sort_index = sort_index + 1') # rubocop:disable Rails/SkipsModelValidations
end

def json_attributes(content_block)
attributes = content_block.attributes
.slice('id', 'block_type', 'content', 'sort_index')
.with_indifferent_access

if content_block.file.attached?
attributes.merge(
fileUrl: Rails.application.routes.url_helpers.rails_blob_path(content_block.file, only_path: true),
filename: content_block.file.filename.to_s
)
else
attributes
end
end
end
end
Expand Down