Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions lib/cadet/course/category.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule Cadet.Course.Category do
@moduledoc """
Category represents a Material category
"""
use Cadet, :model
use Arc.Ecto.Schema

alias Cadet.Accounts.User
alias Cadet.Course.{Category, Material}

schema "categories" do
field(:title, :string)
field(:description, :string)

belongs_to(:uploader, User)
belongs_to(:category, Category)

has_many(:child, Material)
has_many(:sub_category, Category)
timestamps()
end

@required_fields ~w(title)a
@optional_fields ~w(description)a

def changeset(category, attrs \\ %{}) do
category
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> validate_changeset
end

defp validate_changeset(changeset) do
changeset
|> validate_required(@required_fields)
|> foreign_key_constraint(:uploader_id)
|> foreign_key_constraint(:category_id)
end
end
99 changes: 67 additions & 32 deletions lib/cadet/course/course.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule Cadet.Course do
import Ecto.Query

alias Cadet.Accounts.User
alias Cadet.Course.{Group, Material, Upload, Sourcecast}
alias Cadet.Course.{Category, Group, Material, Sourcecast, Upload}

@upload_file_roles ~w(admin staff)a

Expand Down Expand Up @@ -50,7 +50,7 @@ defmodule Cadet.Course do
end

# @doc """
# Reassign a student to a discussion group.
# Reassign a student to a discussion group
# This will un-assign student from the current discussion group
# """
# def assign_group(leader = %User{}, student = %User{}) do
Expand Down Expand Up @@ -117,7 +117,7 @@ defmodule Cadet.Course do
end

@doc """
Delete a sourcecast file.
Delete a sourcecast file
"""
def delete_sourcecast_file(_deleter = %User{role: role}, id) do
if role in @upload_file_roles do
Expand All @@ -136,18 +136,15 @@ defmodule Cadet.Course do
create_material_folder(nil, uploader, attrs)
end

@doc """
Create a new folder to put material files in
"""
def create_material_folder(parent, uploader = %User{}, attrs = %{}) do
def create_material_folder(category, uploader = %User{}, attrs = %{}) do
changeset =
%Material{}
|> Material.folder_changeset(attrs)
%Category{}
|> Category.changeset(attrs)
|> put_assoc(:uploader, uploader)

case parent do
%Material{} ->
Repo.insert(put_assoc(changeset, :parent, parent))
case category do
%Category{} ->
Repo.insert(put_assoc(changeset, :category, category))

_ ->
Repo.insert(changeset)
Expand All @@ -157,38 +154,76 @@ defmodule Cadet.Course do
@doc """
Upload a material file to designated folder
"""
def upload_material_file(folder = %Material{}, uploader = %User{}, attr = %{}) do
changeset =
%Material{}
|> Material.changeset(attr)
|> put_assoc(:uploader, uploader)
|> put_assoc(:parent, folder)
def upload_material_file(uploader = %User{}, attrs = %{}) do
upload_material_file(nil, uploader, attrs)
end

Repo.insert(changeset)
def upload_material_file(category, uploader = %User{role: role}, attrs = %{}) do
if role in @upload_file_roles do
changeset =
%Material{}
|> Material.changeset(attrs)
|> put_assoc(:uploader, uploader)

case category do
%Category{} ->
Repo.insert(put_assoc(changeset, :category, category))

_ ->
Repo.insert(changeset)
end
else
{:error, {:forbidden, "User is not permitted to upload"}}
end
end

@doc """
Delete a material file/directory. A directory tree
is deleted recursively
Delete a material file
"""
def delete_material(id) when is_ecto_id(id) do
material = Repo.get(Material, id)
delete_material(material)
end

def delete_material(material = %Material{}) do
if material.file do
def delete_material(_deleter = %User{role: role}, id) do
if role in @upload_file_roles do
material = Repo.get(Material, id)
Upload.delete({material.file, material})
Repo.delete(material)
else
{:error, {:forbidden, "User is not permitted to delete"}}
end
end

Repo.delete(material)
@doc """
Delete a category
A directory tree is deleted recursively
"""
def delete_category(_deleter = %User{role: role}, id) do
if role in @upload_file_roles do
category = Repo.get(Category, id)
Repo.delete(category)
else
{:error, {:forbidden, "User is not permitted to delete"}}
end
end

@doc """
List material folder content
"""
def list_material_folders(folder = %Material{}) do
import Cadet.Course.Query, only: [material_folder_files: 1]
Repo.all(material_folder_files(folder.id))
def list_material_folders(id) do
import Cadet.Course.Query

mat = id |> material_folder_files() |> Repo.all() |> Repo.preload(:uploader)
cat = id |> category_folder_files() |> Repo.all() |> Repo.preload(:uploader)

Enum.concat(mat, cat)
end

@doc """
Construct directory tree for current folder
"""
def construct_hierarchy(id) do
if is_nil(id) do
[]
else
category = Repo.get(Category, id)
construct_hierarchy(category.category_id) ++ [category]
end
end
end
22 changes: 8 additions & 14 deletions lib/cadet/course/material.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,34 @@ defmodule Cadet.Course.Material do
use Arc.Ecto.Schema

alias Cadet.Accounts.User
alias Cadet.Course.{Material, Upload}
alias Cadet.Course.{Category, Upload}

schema "materials" do
field(:name, :string)
field(:title, :string)
field(:description, :string)
field(:file, Upload.Type)

belongs_to(:parent, Material)
belongs_to(:uploader, User)
belongs_to(:category, Category)

timestamps()
end

@required_fields ~w(name)a
@required_fields ~w(title)a
@optional_fields ~w(description)a
@optional_file_fields ~w(file)a

def folder_changeset(material, attrs \\ %{}) do
material
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_changeset
end
@required_file_fields ~w(file)a

def changeset(material, attrs \\ %{}) do
material
|> cast_attachments(attrs, @optional_file_fields)
|> cast_attachments(attrs, @required_file_fields)
|> cast(attrs, @required_fields ++ @optional_fields)
|> validate_changeset
end

defp validate_changeset(changeset) do
changeset
|> validate_required(@required_fields)
|> foreign_key_constraint(:parent_id)
|> validate_required(@required_fields ++ @required_file_fields)
|> foreign_key_constraint(:uploader_id)
|> foreign_key_constraint(:category_id)
end
end
21 changes: 18 additions & 3 deletions lib/cadet/course/query.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,25 @@ defmodule Cadet.Course.Query do
"""
import Ecto.Query

alias Cadet.Course.Material
alias Cadet.Course.{Category, Material}

def material_folder_files(folder_id) do
Material
|> where([m], m.parent_id == ^folder_id)
if is_nil(folder_id) do
Material
|> where([m], is_nil(m.category_id))
else
Material
|> where([m], m.category_id == ^folder_id)
end
end

def category_folder_files(folder_id) do
if is_nil(folder_id) do
Category
|> where([m], is_nil(m.category_id))
else
Category
|> where([m], m.category_id == ^folder_id)
end
end
end
2 changes: 1 addition & 1 deletion lib/cadet/course/upload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Cadet.Course.Upload do
use Arc.Definition
use Arc.Ecto.Definition

@extension_whitelist ~w(.doc .docx .jpg .pdf .png .ppt .pptx .txt .wav)
@extension_whitelist ~w(.doc .docx .jpg .pdf .png .ppt .pptx .txt .wav .xls .xlsx)
@versions [:original]

def storage_dir(_, _) do
Expand Down
88 changes: 88 additions & 0 deletions lib/cadet_web/controllers/category_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
defmodule CadetWeb.CategoryController do
use CadetWeb, :controller
use PhoenixSwagger

alias Cadet.{Course, Repo}
alias Course.Category

def create(conn, %{"title" => title, "parentId" => category_id}) do
category =
if category_id do
Repo.get(Category, category_id)
else
nil
end

result = Course.create_material_folder(category, conn.assigns.current_user, %{title: title})

case result do
{:ok, _nil} ->
send_resp(conn, 200, "OK")

{:error, {status, message}} ->
conn
|> put_status(status)
|> text(message)
end
end

def create(conn, _params) do
send_resp(conn, :bad_request, "Missing or invalid parameter(s)")
end

def delete(conn, %{"id" => id}) do
result = Course.delete_category(conn.assigns.current_user, id)

case result do
{:ok, _nil} ->
send_resp(conn, 200, "OK")

{:error, {status, message}} ->
conn
|> put_status(status)
|> text(message)
end
end

swagger_path :create do
post("/category")
description("Create folder")
summary("Create folder")
security([%{JWT: []}])

parameters do
material(:body, Schema.ref(:Category), "folder details", required: true)
end

response(200, "Success")
response(400, "Invalid or missing parameter(s)")
response(401, "Unauthorised")
end

swagger_path :delete do
PhoenixSwagger.Path.delete("/category/{id}")
description("Deletes folder by specifying the folder id")
summary("Delete folder")
security([%{JWT: []}])

parameters do
id(:path, :integer, "folder id", required: true)
end

response(200, "Success")
response(400, "Invalid or missing parameter(s)")
response(401, "Unauthorised")
end

def swagger_definitions do
%{
Category:
swagger_schema do
properties do
title(:string, "title", required: true)
description(:string, "description", required: false)
end
end
}
end
end
Loading