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

Copy & Move videos to different courses #1080

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
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
57 changes: 57 additions & 0 deletions api/courses.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func configGinCourseRouter(router *gin.Engine, daoWrapper dao.DaoWrapper) {
{
stream.Use(tools.InitStream(daoWrapper))
stream.GET("/transcodingProgress", routes.getTranscodingProgress)
stream.POST("/copy", routes.copyStream)
}

stats := courses.Group("/stats")
Expand Down Expand Up @@ -1516,6 +1517,62 @@ type copyCourseRequest struct {
YearW string
}

func (r coursesRoutes) copyStream(c *gin.Context) {
type req struct {
TargetCourse uint `json:"targetCourse"`
Move bool `json:"move"`
}
var request req
err := c.BindJSON(&request)
if err != nil {
_ = c.Error(tools.RequestError{Status: http.StatusBadRequest, CustomMessage: "Bad request", Err: err})
return
}
tlctx := c.MustGet("TUMLiveContext").(tools.TUMLiveContext)

isAdmin := tlctx.User.Role == model.AdminType

if !isAdmin {
targetCourseAdmins, err := r.DaoWrapper.CoursesDao.GetCourseAdmins(request.TargetCourse)
if err != nil {
log.WithError(err).Error("Error getting course admins")
_ = c.Error(tools.RequestError{
Status: http.StatusInternalServerError,
CustomMessage: "can't determine admins of target course",
Err: err,
})
}
for _, admin := range targetCourseAdmins {
if admin.ID == tlctx.User.ID {
isAdmin = true
break
}
}
}
if !isAdmin {
_ = c.Error(tools.RequestError{
Status: http.StatusForbidden,
CustomMessage: "you are not admin of the target course",
})
return
}

stream := tlctx.Stream
stream.Model = gorm.Model{}
stream.CourseID = request.TargetCourse
err = r.StreamsDao.CreateStream(stream)
if err != nil {
_ = c.Error(tools.RequestError{
Status: http.StatusInternalServerError,
CustomMessage: "Can't save stream",
Err: err,
})
}
if request.Move {
r.StreamsDao.DeleteStream(strconv.Itoa(int(tlctx.Stream.ID)))
}
}

func (r coursesRoutes) copyCourse(c *gin.Context) {
var request copyCourseRequest
err := c.BindJSON(&request)
Expand Down
1 change: 1 addition & 0 deletions model/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ func (s Stream) getJson(lhs []LectureHall, course Course) gin.H {
"courseSlug": course.Slug,
"private": s.Private,
"downloadableVods": s.GetVodFiles(),
"isCopying": false,
}
}

Expand Down
3 changes: 2 additions & 1 deletion web/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func (r mainRoutes) EditCoursePage(c *gin.Context) {
Semesters: semesters,
CurY: tumLiveContext.Course.Year,
CurT: tumLiveContext.Course.TeachingTerm,
EditCourseData: EditCourseData{IndexData: indexData, IngestBase: tools.Cfg.IngestBase, LectureHalls: lectureHalls},
EditCourseData: EditCourseData{Courses: courses, IndexData: indexData, IngestBase: tools.Cfg.IngestBase, LectureHalls: lectureHalls},
})
if err != nil {
log.Printf("%v\n", err)
Expand Down Expand Up @@ -331,6 +331,7 @@ type EditCourseData struct {
IndexData IndexData
IngestBase string
LectureHalls []model.LectureHall
Courses []model.Course // administered courses of user
}

type LectureUnitsPageData struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{{- /*gotype: github.com/joschahenningsen/TUM-Live/web.AdminPageData*/ -}}

{{$course := .IndexData.TUMLiveContext.Course}}
{{$courses := .Courses}}
{{$user := .IndexData.TUMLiveContext.User}}
{{$ingestBase := .IngestBase}}
{{$lectureHalls := .LectureHalls}}
Expand Down Expand Up @@ -199,6 +200,10 @@
:class="lecture.private?'text-gray-400 dark:hover:text-gray-500 hover:text-gray-300':'text-red-400 dark:hover:text-red-500 hover:text-red-300'">
Make private
</button>
<button class="block w-full px-4 py-2 text-left text-sm text-gray-400 dark:hover:text-gray-500 hover:text-gray-300"
@click="lecture.isCopying = true; closeMoreDropdown()">
Move/Copy to other course
</button>
</div>
<template x-if="lecture.seriesIdentifier.length > 0">
<div class="border-t border-black">
Expand All @@ -217,7 +222,7 @@
</div>
</div>

<button x-show="lecture.uiEditMode == 0"
<button x-show="lecture.uiEditMode == 0 && !lecture.isCopying"
@click="lecture.startSingleEdit(); closeMoreDropdown();"
class="bg-gray-100 dark:bg-gray-900 w-full py-2 mt-2 rounded-b">
<span class="text-gray-700 dark:text-gray-300">Edit Lecture</span>
Expand Down Expand Up @@ -451,5 +456,53 @@
</div>
</div>
</div>

<template x-if="lecture.isCopying">
<form class="p-4" x-data="{move: false, selected: -1, success: undefined}"
@submit.prevent="fetch(`/api/course/${lecture.courseId}/stream/${lecture.lectureId}/copy`,
{method: 'POST', body: JSON.stringify({targetCourse: selected, move: move})}).then(r=>{success=r.ok})">
<div x-show="success===undefined">
<div class="w-full grid grid-cols-2">
<button @click="move=false" class="w-full py-2 mt-2 rounded" :class="!move && 'bg-indigo-500/70'" type="button">
<span class="text-gray-700 dark:text-gray-300">Copy</span>
</button>
<button @click="move=true" class="w-full py-2 mt-2 rounded" :class="move && 'bg-indigo-500/70'" type="button">
<span class="text-gray-700 dark:text-gray-300">Move</span>
</button>
</div>
<span class="text-3 mt-3">Select target course:</span>
<div class="flex flex-col m-2 mb-4">
{{range $c := $courses}}
{{if ne $c.ID $course.ID}}
<label class="w-full">
<input name="targetcourse" type="radio" @click="selected={{$c.ID}}">
<span>
{{$c.Name}}
<span class="font-semibold">{{$c.Year}} | {{$c.TeachingTerm}}</span>
</span>
</label>
{{end}}
{{end}}
</div>
<button @click="lecture.isCopying = false"
type="button"
class="px-8 py-3 text-2 text-white rounded bg-indigo-500/70 hover:bg-indigo-500/90 dark:bg-indigo-500/10 disabled:opacity-20 dark:hover:bg-indigo-500/20 mr-4">
Cancel
</button>

<button type="submit"
class="px-8 py-3 text-2 text-white rounded bg-indigo-500/70 hover:bg-indigo-500/90 dark:bg-indigo-500/10 disabled:opacity-20 dark:hover:bg-indigo-500/20 mr-4"
:disabled="selected === -1">
Done
</button>
</div>
<div x-show="success===true">
<span class="text-success font-semibold">Lecture copied successfully.</span>
</div>
<div x-show="success===false">
<span class="text-red-500 font-semibold">Lecture could not be copied!</span>
</div>
</form>
</template>
</li>
{{end}}