diff --git a/apiGo/api/Helpers.go b/apiGo/api/Helpers.go index aac7b93..1696c57 100644 --- a/apiGo/api/Helpers.go +++ b/apiGo/api/Helpers.go @@ -15,7 +15,8 @@ func readVideosFromResultset(rows *sql.Rows) []types.VideoUnloadedType { var vid types.VideoUnloadedType err := rows.Scan(&vid.MovieId, &vid.MovieName) if err != nil { - panic(err.Error()) // proper error handling instead of panic in your app + fmt.Println(err.Error()) + return nil } result = append(result, vid) } diff --git a/apiGo/api/Settings.go b/apiGo/api/Settings.go index 711d4de..b3a6a63 100644 --- a/apiGo/api/Settings.go +++ b/apiGo/api/Settings.go @@ -67,12 +67,13 @@ func getSettingsFromDB() { sett := settings.LoadSettings() type InitialDataTypeResponse struct { - DarkMode bool - Pasword bool - MediacenterName string - VideoPath string - TVShowPath string - TVShowEnabled bool + DarkMode bool + Pasword bool + MediacenterName string + VideoPath string + TVShowPath string + TVShowEnabled bool + FullDeleteEnabled bool } regexMatchUrl := regexp.MustCompile("^http(|s)://([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}:[0-9]{1,5}") @@ -82,12 +83,13 @@ func getSettingsFromDB() { serverTVShowPath := strings.TrimPrefix(sett.TVShowPath, tvshowurl) res := InitialDataTypeResponse{ - DarkMode: sett.DarkMode, - Pasword: sett.Pasword != "-1", - MediacenterName: sett.MediacenterName, - VideoPath: serverVideoPath, - TVShowPath: serverTVShowPath, - TVShowEnabled: settings.TVShowsEnabled(), + DarkMode: sett.DarkMode, + Pasword: sett.Pasword != "-1", + MediacenterName: sett.MediacenterName, + VideoPath: serverVideoPath, + TVShowPath: serverTVShowPath, + TVShowEnabled: settings.TVShowsEnabled(), + FullDeleteEnabled: settings.VideosDeletable(), } str, _ := json.Marshal(res) diff --git a/apiGo/api/Video.go b/apiGo/api/Video.go index 9e6c1d2..373d3a2 100644 --- a/apiGo/api/Video.go +++ b/apiGo/api/Video.go @@ -6,6 +6,8 @@ import ( "net/url" "openmediacenter/apiGo/api/types" "openmediacenter/apiGo/database" + "openmediacenter/apiGo/database/settings" + "os" "strconv" ) @@ -418,12 +420,14 @@ func addToVideoHandlers() { * @apiGroup video * * @apiParam {int} MovieId ID of video + * @apiParam {bool} FullyDelete Delete video from disk? * * @apiSuccess {string} result 'success' if successfully or error message if not */ AddHandler("deleteVideo", VideoNode, func(info *HandlerInfo) []byte { var args struct { - MovieId int + MovieId int + FullyDelete bool } if err := FillStruct(&args, info.Data); err != nil { fmt.Println(err.Error()) @@ -443,6 +447,26 @@ func addToVideoHandlers() { return database.ManualSuccessResponse(err) } + // only allow deletion of video if cli flag is set, independent of passed api arg + if settings.VideosDeletable() && args.FullyDelete { + // get physical path of video to delete + query = fmt.Sprintf("SELECT movie_url FROM videos WHERE movie_id=%d", args.MovieId) + var vidpath string + err := database.QueryRow(query).Scan(&vidpath) + if err != nil { + return database.ManualSuccessResponse(err) + } + + assembledPath := database.SettingsVideoPrefix + "/" + vidpath + + err = os.Remove(assembledPath) + if err != nil { + fmt.Printf("unable to delete file: %s -- %s\n", assembledPath, err.Error()) + return database.ManualSuccessResponse(err) + } + } + + // delete video row from db query = fmt.Sprintf("DELETE FROM videos WHERE movie_id=%d", args.MovieId) return database.SuccessQuery(query) }) diff --git a/apiGo/database/settings/Settings.go b/apiGo/database/settings/Settings.go index 409a071..d13d305 100644 --- a/apiGo/database/settings/Settings.go +++ b/apiGo/database/settings/Settings.go @@ -1,6 +1,7 @@ package settings var tvShowEnabled bool +var videosDeletable bool func TVShowsEnabled() bool { return tvShowEnabled @@ -9,3 +10,11 @@ func TVShowsEnabled() bool { func SetTVShowEnabled(enabled bool) { tvShowEnabled = enabled } + +func VideosDeletable() bool { + return videosDeletable +} + +func SetVideosDeletable(deletable bool) { + videosDeletable = deletable +} diff --git a/apiGo/main.go b/apiGo/main.go index 80e138c..9d96e0e 100644 --- a/apiGo/main.go +++ b/apiGo/main.go @@ -57,10 +57,12 @@ func handleCommandLineArguments() (*database.DatabaseConfig, bool, *string) { pathPrefix := flag.String("ReindexPrefix", "/var/www/openmediacenter", "Prefix path for videos to reindex") disableTVShowSupport := flag.Bool("DisableTVSupport", false, "Disable the TVShow support and pages") + videosFullyDeletable := flag.Bool("FullyDeletableVideos", false, "Allow deletion from harddisk") flag.Parse() settings2.SetTVShowEnabled(!*disableTVShowSupport) + settings2.SetVideosDeletable(*videosFullyDeletable) return &database.DatabaseConfig{ DBHost: *dbhostPtr, diff --git a/package.json b/package.json index 6bc2499..1890f28 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "build": "CI=false react-scripts build", "test": "CI=true react-scripts test --reporters=jest-junit --verbose --silent --coverage --reporters=default", "lint": "eslint --format gitlab src/", - "apidoc": "apidoc -i apiGo/ -o doc/" + "apidoc": "apidoc --single -i apiGo/ -o doc/" }, "jest": { "collectCoverageFrom": [ diff --git a/src/App.tsx b/src/App.tsx index db339d0..d78d444 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -87,6 +87,7 @@ class App extends React.Component<{}, state> { GlobalInfos.setVideoPaths(result.VideoPath, result.TVShowPath); GlobalInfos.setTVShowsEnabled(result.TVShowEnabled); + GlobalInfos.setFullDeleteEnabled(result.FullDeleteEnabled); this.setState({ mediacentername: result.MediacenterName diff --git a/src/elements/Popups/ButtonPopup/ButtonPopup.test.js b/src/elements/Popups/ButtonPopup/ButtonPopup.test.js new file mode 100644 index 0000000..9ce12ea --- /dev/null +++ b/src/elements/Popups/ButtonPopup/ButtonPopup.test.js @@ -0,0 +1,51 @@ +import {shallow} from 'enzyme'; +import React from 'react'; +import {ButtonPopup} from './ButtonPopup'; +import exp from "constants"; + +describe('', function () { + it('renders without crashing ', function () { + const wrapper = shallow(); + wrapper.unmount(); + }); + + it('renders two buttons', function () { + const wrapper = shallow(); + expect(wrapper.find('Button')).toHaveLength(2); + }); + + it('renders three buttons if alternative defined', function () { + const wrapper = shallow(); + expect(wrapper.find('Button')).toHaveLength(3); + }); + + it('test click handlings', function () { + const althandler = jest.fn(); + const denyhandler = jest.fn(); + const submithandler = jest.fn(); + + const wrapper = shallow(); + wrapper.find('Button').findWhere(e => e.props().title === "deny").simulate('click'); + expect(denyhandler).toHaveBeenCalledTimes(1); + + wrapper.find('Button').findWhere(e => e.props().title === "alt").simulate('click'); + expect(althandler).toHaveBeenCalledTimes(1); + + wrapper.find('Button').findWhere(e => e.props().title === "submit").simulate('click'); + expect(submithandler).toHaveBeenCalledTimes(1); + }); + + it('test Parentsubmit and parenthide callbacks', function () { + const ondeny = jest.fn(); + const onsubmit = jest.fn(); + + const wrapper = shallow(); + wrapper.find('PopupBase').props().onHide(); + expect(ondeny).toHaveBeenCalledTimes(1); + + wrapper.find('PopupBase').props().ParentSubmit(); + expect(onsubmit).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/elements/Popups/ButtonPopup/ButtonPopup.tsx b/src/elements/Popups/ButtonPopup/ButtonPopup.tsx new file mode 100644 index 0000000..3b5b667 --- /dev/null +++ b/src/elements/Popups/ButtonPopup/ButtonPopup.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import PopupBase from '../PopupBase'; +import {Button} from '../../GPElements/Button'; + +/** + * Delete Video popup + * can only be rendered once! + * @constructor + */ +export const ButtonPopup = (props: { + onSubmit: () => void; + onDeny: () => void; + onAlternativeButton?: () => void; + SubmitButtonTitle: string; + DenyButtonTitle: string; + AlternativeButtonTitle?: string; + Title: string; +}): JSX.Element => { + return ( + <> + props.onDeny()} + height='200px' + width='400px' + ParentSubmit={(): void => { + props.onSubmit(); + }}> +