summaryrefslogtreecommitdiff
path: root/Assistant/WebApp/DashBoard.hs
blob: 5bbcee3c92714cfe9a7055a4fb240575e2534b9a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
{- git-annex assistant webapp dashboard
 -
 - Copyright 2012 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU AGPL version 3 or higher.
 -}

{-# LANGUAGE CPP, TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}

module Assistant.WebApp.DashBoard where

import Assistant.WebApp.Common
import Assistant.WebApp.RepoList
import Assistant.WebApp.Notifications
import Assistant.TransferQueue
import Assistant.TransferSlots
import Assistant.DaemonStatus
import Utility.NotificationBroadcaster
import Types.Transfer
import Logs.Transfer
import Utility.Percentage
import Utility.DataUnits
import qualified Remote
import qualified Git

import qualified Text.Hamlet as Hamlet
import qualified Data.Map as M
import Control.Concurrent

{- A display of currently running and queued transfers. -}
transfersDisplay :: Widget
transfersDisplay = do
	current <- liftAssistant $ M.toList <$> getCurrentTransfers
	queued <- take 10 <$> liftAssistant getTransferQueue
	autoUpdate ident NotifierTransfersR (10 :: Int) (10 :: Int)
	let transfers = simplifyTransfers $ current ++ queued
	let transfersrunning = not $ null transfers
	scanrunning <- if transfersrunning
		then return False
		else liftAssistant $ transferScanRunning <$> getDaemonStatus
	$(widgetFile "dashboard/transfers")
  where
	ident = "transfers"
	isrunning info = not $
		transferPaused info || isNothing (startedTime info)
	desc transfer info = case associatedFile info of
		AssociatedFile Nothing -> serializeKey $ transferKey transfer
		AssociatedFile (Just af) -> fromRawFilePath af

{- Simplifies a list of transfers, avoiding display of redundant
 - equivalent transfers. -}
simplifyTransfers :: [(Transfer, TransferInfo)] -> [(Transfer, TransferInfo)]
simplifyTransfers [] = []
simplifyTransfers (x:[]) = [x]
simplifyTransfers (v@(t1, _):r@((t2, _):l))
	| equivilantTransfer t1 t2 = simplifyTransfers (v:l)
	| otherwise = v : simplifyTransfers r

{- Called by client to get a display of currently in process transfers.
 -
 - Returns a div, which will be inserted into the calling page.
 -
 - Note that the head of the widget is not included, only its
 - body is. To get the widget head content, the widget is also 
 - inserted onto the getDashboardR page.
 -}
getTransfersR :: NotificationId -> Handler Html
getTransfersR nid = do
	waitNotifier getTransferBroadcaster nid

	p <- widgetToPageContent transfersDisplay
	withUrlRenderer $ [hamlet|^{pageBody p}|]

{- The main dashboard. -}
dashboard :: Bool -> Widget
dashboard warnNoScript = do
	let repolist = repoListDisplay $
		mainRepoSelector { nudgeAddMore = True }
	let transferlist = transfersDisplay
	$(widgetFile "dashboard/main")

getDashboardR :: Handler Html
getDashboardR = ifM inFirstRun
	( redirect ConfigurationR
	, page "" (Just DashBoard) $ dashboard True
	)

{- Used to test if the webapp is running. -}
headDashboardR :: Handler ()
headDashboardR = noop

{- Same as DashboardR, except no autorefresh at all (and no noscript warning). -}
getNoScriptR :: Handler Html
getNoScriptR = page "" (Just DashBoard) $ dashboard False

{- Same as DashboardR, except with autorefreshing via meta refresh. -}
getNoScriptAutoR :: Handler Html
getNoScriptAutoR = page "" (Just DashBoard) $ do
	let delayseconds = 3 :: Int
	let this = NoScriptAutoR
	toWidgetHead $(Hamlet.hamletFile $ hamletTemplate "dashboard/metarefresh")
	dashboard False

{- The javascript code does a post. -}
postFileBrowserR :: Handler ()
postFileBrowserR = void openFileBrowser

{- Used by non-javascript browsers, where clicking on the link actually
 - opens this page, so we redirect back to the referrer. -}
getFileBrowserR :: Handler ()
getFileBrowserR = whenM openFileBrowser redirectBack

{- Opens the system file browser on the repo, or, as a fallback,
 - goes to a file:// url. Returns True if it's ok to redirect away
 - from the page (ie, the system file browser was opened). 
 -
 - Note that the command is opened using a different thread, to avoid
 - blocking the response to the browser on it. -}
openFileBrowser :: Handler Bool
openFileBrowser = do
	path <- fromRawFilePath 
		<$> (liftIO . absPath =<< liftAnnex (fromRepo Git.repoPath))
#ifdef darwin_HOST_OS
	let cmd = "open"
	let p = proc cmd [path]
#else
#ifdef mingw32_HOST_OS
	{- Changing to the directory and then opening . works around
	 - spaces in directory name, etc. -}
	let cmd = "cmd"
	let p = (proc cmd ["/c start ."]) { cwd = Just path }
#else
	let cmd = "xdg-open"
	let p = proc cmd [path]
#endif
#endif
	ifM (liftIO $ inSearchPath cmd)
		( do
			let run = void $ liftIO $ forkIO $ do
				withCreateProcess p $ \_ _ _ pid -> void $
					waitForProcess pid
			run
#ifdef mingw32_HOST_OS
			{- On windows, if the file browser is not
			 - already open, it comes up below the
			 - web browser when started. 
			 -
			 - Running it a second time brings it
			 - to the foreground.
			 -
			 - Seems to need a delay long enough for the file
			 - browser to be open in order to work. Here 1
			 - second. -}
			liftIO $ threadDelay 1000000
			run
#endif
			return True
		, do
			void $ redirect $ "file://" ++ path
			return False
		)

{- Transfer controls. The GET is done in noscript mode and redirects back
 - to the referring page. The POST is called by javascript. -}
getPauseTransferR :: Transfer -> Handler ()
getPauseTransferR = noscript postPauseTransferR
postPauseTransferR :: Transfer -> Handler ()
postPauseTransferR = liftAssistant . pauseTransfer
getStartTransferR :: Transfer -> Handler ()
getStartTransferR = noscript postStartTransferR
postStartTransferR :: Transfer -> Handler ()
postStartTransferR = liftAssistant . startTransfer
getCancelTransferR :: Transfer -> Handler ()
getCancelTransferR = noscript postCancelTransferR
postCancelTransferR :: Transfer -> Handler ()
postCancelTransferR = liftAssistant . cancelTransfer False

noscript :: (Transfer -> Handler ()) -> Transfer -> Handler ()
noscript a t = a t >> redirectBack