diff --git a/Examples/AllExamples.icl b/Examples/AllExamples.icl index d8733de7f3040d0b03dde7f38a746d5690cae172..66acbc111090071d8b26083a6db24e0aba5a0583 100644 --- a/Examples/AllExamples.icl +++ b/Examples/AllExamples.icl @@ -37,9 +37,6 @@ import SharedVariables //Graphical iTask Notation //import GinExamples -//Ad-hoc work extensions -import Groups, Lists, Messages, Consensus - //Client import WorkflowAdmin @@ -65,7 +62,6 @@ where //, changeHandlingExample , changeExamples , sharedValueExamples - , [workflow "Examples/General/Ask opinions" "Gather opinions regarding a specific subject" askOpinions] //, rpcExamples //, ginExamples , apiDocumentationExamples diff --git a/Server/API/Core/IntegrationTasks.icl b/Server/API/Core/IntegrationTasks.icl index b4cc6ba25f366f9ee2cc76b097583b04c95a8460..bd25ff05478c6cd40b66fe9242f9efb5216ac41b 100644 --- a/Server/API/Core/IntegrationTasks.icl +++ b/Server/API/Core/IntegrationTasks.icl @@ -4,7 +4,7 @@ import StdInt, StdFile, StdTuple, StdList import Directory, File, FilePath, Error, OSError, UrlEncoding, Text, Tuple -import SystemTypes, IWorld, Task, TaskContext, Config +import SystemTypes, IWorld, Task, TaskContext import ExceptionCombinators, TuningCombinators import InteractionTasks import Shared @@ -40,7 +40,7 @@ callProcess cmd args where //Start the process init :: TaskNr *IWorld -> (!TaskContextTree,!*IWorld) - init taskNr iworld =:{IWorld | config, tmpDirectory, world} + init taskNr iworld =:{IWorld | sdkDirectory, tmpDirectory, world} # outfile = tmpDirectory (iTaskId taskNr "callprocess") # context = TCBasic 'Map'.newMap # asyncArgs = [ "--taskid" @@ -51,7 +51,7 @@ where , cmd ] ++ args - # (res,world) = 'Process'.runProcess config.Config.runAsyncPath asyncArgs Nothing world + # (res,world) = 'Process'.runProcess (sdkDirectory "Tools" "RunAsync" "RunAsync.exe") asyncArgs Nothing world = case res of Error e = (setLocalVar "error" e context, {IWorld|iworld & world = world}) Ok _ = (setLocalVar "outfile" outfile context, {IWorld|iworld & world = world}) @@ -122,13 +122,13 @@ where initRPC = mkInstantTask("Call RPC", "Initializing") eval - eval taskNr iworld=:{IWorld|config,tmpDirectory,world} + eval taskNr iworld=:{IWorld|sdkDirectory,tmpDirectory,world} # infile = tmpDirectory (mkFileName taskNr "request") # outfile = tmpDirectory (mkFileName taskNr "response") # (res,world) = writeFile infile request world | isError res = (taskException (RPCException ("Write file " +++ infile +++ " failed: " +++ toString (fromError res))),{IWorld|iworld & world = world}) - # cmd = config.Config.curlPath + # cmd = sdkDirectory "Tools" "Curl" "curl.exe" # args = [ options , "--data-binary" , "@" +++ infile diff --git a/Server/API/Core/SystemData.dcl b/Server/API/Core/SystemData.dcl index b02a8e7616ee01f6c991c2cee32c5c827c57f9c4..ed7fc19a35b5f8838ff04d430af3c297b0e0453e 100644 --- a/Server/API/Core/SystemData.dcl +++ b/Server/API/Core/SystemData.dcl @@ -5,9 +5,9 @@ definition module SystemData */ import Maybe from SharedCombinators import :: ReadWriteShared, :: ReadOnlyShared, :: Shared -from SystemTypes import :: DateTime, :: Date, :: Time, :: User, :: Role, :: UserDetails, :: TaskList, :: Tree, :: ProcessId, :: TaskInstanceMeta +from SystemTypes import :: DateTime, :: Date, :: Time, :: User, :: Role, :: UserDetails, :: TaskList, :: Tree +from SystemTypes import :: ProcessId, :: TaskInstanceMeta, :: Config from Void import :: Void -from Config import :: Config // Date & time currentDateTime :: ReadOnlyShared DateTime diff --git a/Server/API/Core/SystemData.icl b/Server/API/Core/SystemData.icl index 8ab23e9ca7eea681486fc8c4ce7e3a1fc8c33eb6..92aec4cb8b3a3795876fda5c94e72eaa85c3a658 100644 --- a/Server/API/Core/SystemData.icl +++ b/Server/API/Core/SystemData.icl @@ -8,7 +8,6 @@ from IWorld import :: IWorld(..), :: Control from Util import qualified currentDate, currentTime, currentDateTime, currentTimestamp from WorkflowDB import qualified class WorkflowDB(..), instance WorkflowDB IWorld from WorkflowDB import :: WorkflowDescription -from Config import :: Config currentDateTime :: ReadOnlyShared DateTime currentDateTime = makeReadOnlyShared "SystemData_currentDateTime" 'Util'.currentDateTime 'Util'.currentTimestamp diff --git a/Server/API/Core/SystemTypes.dcl b/Server/API/Core/SystemTypes.dcl index fd2e331937fa780127b3188fc3e350c1f4a89b80..bd5ddebcf1928bbde4599c85d2698512c881b425 100644 --- a/Server/API/Core/SystemTypes.dcl +++ b/Server/API/Core/SystemTypes.dcl @@ -383,21 +383,11 @@ noMeta :: ManagementMeta //Configuration :: Config = - { clientPath :: !String // Where is the client located. - , staticPath :: !String // Additional location where statically served content may be placed - , rootPassword :: !String // Password for the 'root' superuser (default 'root'). + { rootPassword :: !String // Password for the 'root' superuser (default 'root'). , rootEmail :: !String // E-mail address for the 'root' superuser (default root@localhost). , sessionTime :: !Int // Time (in seconds) before inactive sessions are garbage collected. Default is 3600 (one hour). - , serverPort :: !Int // The TCP port the server runs on. Default is 80. - , serverPath :: !String // The path at which the services are served (default /services) - , debug :: !Bool // Run the server in debug mode (default False). , smtpServer :: !String // The smtp server to use for sending e-mails - , generalWorkflows :: !Bool // Enable the "general" workflows for managing ad-hoc work - , runAsyncPath :: !String // Path to RunAsync tool for running asynchronous OS tasks and timers. - , curlPath :: !String // Path to Curl needed for RPC tasks. } - - /* * Gives the unique username of a user * diff --git a/Server/API/Extensions/Consensus.dcl b/Server/API/Extensions/Consensus.dcl deleted file mode 100644 index 7263e7a47155a3ca014fc1ea61495fd856e1226a..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Consensus.dcl +++ /dev/null @@ -1,12 +0,0 @@ -definition module Consensus -/** -* This extension provides the possibility to poll a set of users -* for their opinion on some topic to come to a shared agreement. -*/ - -import iTasks - -/** -* Top level flow for asking people for opinions. -*/ -askOpinions :: Task Void \ No newline at end of file diff --git a/Server/API/Extensions/Consensus.icl b/Server/API/Extensions/Consensus.icl deleted file mode 100644 index e3c5c419b451bf1a7883f3a2a98f26f2ba824d91..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Consensus.icl +++ /dev/null @@ -1,92 +0,0 @@ -implementation module Consensus - -import iTasks, GenEq -import Messages, Groups - -:: Topic = { topic :: String, description :: Maybe Note} -:: Results a :== [(a,[(User,String)])] - -derive class iTask Topic - -askOpinions :: Task Void -askOpinions - = defineTopic - >>= \topic -> - defineItemType - >>= \type -> case type of - "Date" = askOpinionsDate topic >>| return Void - "Document" = askOpinionsDocument topic >>| return Void - "Other" = askOpinionsOther topic >>| return Void - -//The type dependent second part of the flow -askOpinionsGeneric :: Topic -> Task (Results a) | iTask a -askOpinionsGeneric topic - = defineItems - >>= \items -> - defineAnswers - >>= \answers -> - defineParticipants - >>= \participants -> - collectOpinions topic items answers participants - >>= showResult - -askOpinionsDate :: Topic -> Task (Results Date) -askOpinionsDate topic = askOpinionsGeneric topic - -askOpinionsDocument :: Topic -> Task (Results Document) -askOpinionsDocument topic = askOpinionsGeneric topic - -askOpinionsOther :: Topic -> Task (Results String) -askOpinionsOther topic = askOpinionsGeneric topic - -defineTopic :: Task Topic -defineTopic - = enterInformation ("Define topic","Please define the topic that you would like to get opinions about") [] - -defineItemType :: Task String -defineItemType - = enterChoice ("Define item type","What type of item(s) would you like to get opinions about") [] ["Date","Document","Other"] - -defineItems :: Task [a] | iTask a -defineItems - = enterInformation ("Define items","Enter the item(s) you would like to get opinions about") [] - -defineAnswers :: Task [String] -defineAnswers - = enterInformation ("Define answers","Please define the available answer choices") [] - -||- - enterChoice ("Common answers","Or select one of these common answer sets") [] - [["Yes","No"],["Yes","No","Maybe"],["I agree","I disagree"],["1","2","3","4","5"]] - -defineParticipants :: Task [User] -defineParticipants - = getMyGroups - >>= \groups -> case groups of - [] = enterInformation ("Define people","Enter the people you would like to ask for an opinion") [] - _ = (enterChoice ("Choose a group","Choose a group...") [] groups >>= transform (\group -> group.members)) - -||- - (enterInformation ("Define people","Or enter individual people to ask for an opinion") []) - -collectOpinions :: Topic [a] [String] [User] -> Task (Results a) | iTask a -collectOpinions topic items answers participants - = ( Description "Collecting opinions..." @>> - Description "Waiting for everyone to give their opinion" @>> - allTasks [collectOpinion topic user items answers \\ user <- participants ] - ) - >>= transform (orderByItem items) -where - orderByItem :: [a] [(User,[(a,String)])] -> [(a,[(User,String)])] - orderByItem items opinions = [(item, [(user, snd (options !! i)) \\ (user,options) <- opinions ]) \\ item <- items & i <- [0..]] - -collectOpinion :: Topic User [a] [String] -> Task (User,[(a,String)]) | iTask a -collectOpinion topic user items answers - = user @: - (Description ("Your opinion about: " +++ topic.topic) @>> - (allTasks [enterChoice ("Option " <+++ i,"What is your opinion about:") [ChoiceContext item] answers \\ item <- items & i <- [1..]] >>= transform (merge items))) -where - merge items opinions = (user,zip (items,opinions)) - -showResult :: (Results a) -> Task (Results a) | iTask a -showResult result = viewInformation ("Opinions","The results of your opinion request:") [] result - - diff --git a/Server/API/Extensions/Groups.dcl b/Server/API/Extensions/Groups.dcl deleted file mode 100644 index 26ec6a1ef95c7b2263aa98a062570df1cf0b2183..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Groups.dcl +++ /dev/null @@ -1,94 +0,0 @@ -definition module Groups -/** -* This extension provides the possibility to create groups of users. -* -* - Users can create new groups (they automatically become member) -* - Group members can invite others to join a group -* - Group members can leave a group (when the last member is removed from a group it is deleted) -*/ - -import iTasks - -:: Group = - { groupId :: !Hidden Int - , name :: !String - , members :: ![User] - } - -derive class iTask Group - -instance DB Group -instance toString Group - -/** -* Top level workflow for users to manage their own group -* -*/ -manageGroups :: Task Void -/** -* Management workflow for a single group. -* -* @param The group to manage -*/ -manageGroup :: !Group -> Task Void -/** -* Create a new group -* -* @param A name for the group -* @param The first member of the group -* -* @return The newly created group -*/ -createGroup :: !String !User -> Task Group -/** -* Retrieve all groups that the current user is a member of. -* -* @return The list of groups -*/ -getMyGroups :: Task [Group] -/** -* Retrieve all groups -* -* @return The list of groups -*/ -getAllGroups :: Task [Group] -/** -* Delete a group from the system. -* This is not really neccesary because groups are automatically deleted when -* the last member is removed from them. -* -* @param The group to delete -* -* @return The deleted group -*/ -deleteGroup :: !Group -> Task Group -/** -* Add a user to a group. Groups can not contain duplicates. -* -* @param The group to add the user to -* @param The user to add to the group -* -* @return The (updated) group -*/ -addMemberToGroup :: !Group !User -> Task Group -/** -* Remove a user from a group. -* When the last user is removed it is deleted from the system. -* -* @param The group to remove the user from -* @param The user to remove from the group -* -* @return The (updated) group -*/ -removeMemberFromGroup :: !Group !User -> Task Group -/** -* Ask a user if (s)he wants to join the group. -* If the invitation is accepted the user is added to the group. -* If it is declined, the group is unchanged. -* -* @param The group to invite the user to -* @param The user to invite -* -* @param The (possibly) updated group -*/ -inviteUserToGroup :: !Group !User -> Task Group diff --git a/Server/API/Extensions/Groups.icl b/Server/API/Extensions/Groups.icl deleted file mode 100644 index 6432f6e73d526dd7ea1b3f94da14a3b1f4671286..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Groups.icl +++ /dev/null @@ -1,120 +0,0 @@ -implementation module Groups - -import iTasks - -derive class iTask Group - -instance DB Group -where - databaseId = sharedStore "Groups" [] - getItemId g = DBRef (fromHidden g.Group.groupId) - setItemId (DBRef groupId) g = {Group| g & groupId = toHidden groupId} - -instance toString Group where toString g = g.Group.name - -manageGroups :: Task Void -manageGroups - = Description "Manage groups" @>> - ( getMyGroups - >>= overview - >>= \res -> case res of - (ActionOpen,Just group) = manageGroup group >>| return False - (ActionNew,_) = newGroup >>= manageGroup >>| return False - (ActionQuit,_) = return True - ) >| return Void -where - overview [] = viewInformation ("My groups",startMsg) [] Void >>+ \_ -> UserActions [(ActionNew,Just (ActionNew,Nothing)),(ActionQuit,Just (ActionQuit,Nothing))] - overview list = enterChoice ("My groups",listMsg) [] list >>+ (\{modelValue,localValid} -> let mbG = if localValid (Just modelValue) Nothing in UserActions [aOpen mbG,aNew,aQuit]) - - aOpen mbG = (ActionOpen, maybe Nothing (\g -> Just (ActionOpen,Just g)) mbG) - aNew = (ActionNew, Just (ActionNew,Nothing)) - aQuit = (ActionQuit, Just (ActionQuit,Nothing)) - newGroup = enterInformation ("New group","Please enter a name for the new group") [] - >>= \name -> - get currentUser - >>= \user -> - createGroup name user - - startMsg = [Text "You have no groups yet.",BrTag [], BrTag [] - ,Text "You can create your own user groups to which you can invite other users", BrTag [] - ,Text "Members of a group can easily send each other messages " - ,Text "or ask each others opinions." - ] - - listMsg = [Text "You are a member of the groups listed below.", BrTag [], BrTag [] - ,Text "You may select one to view it or create a new group." - ] - -manageGroup :: !Group -> Task Void -manageGroup igroup - = - ( justdo (dbReadItem (getItemId igroup)) - >>= \group -> - viewInformation (toString group,"This group contains the following members:") [] group.members - >?* [(ActionClose, Always (return True)) - ,(Action "Invite new member", Always (invite group >>| return False)) - ,(Action "Leave group", Always (leave group >>| return False)) - ] - ) >| return Void -where - invite group - = enterInformation ("Invite a someone to join " +++ toString group,"Please enter a user to invite to the group") [] - >>= inviteUserToGroup group - - leave group - = get currentUser - >>= removeMemberFromGroup group - -createGroup :: !String !User -> Task Group -createGroup name user - = dbCreateItem {Group | groupId = Hidden 0, name = name, members = [user]} - -getAllGroups :: Task [Group] -getAllGroups - = dbReadAll - -getMyGroups :: Task [Group] -getMyGroups = get currentUser >>= \user -> dbReadAll >>= transform (filter (groupMember user)) -where - groupMember user {Group|members} = isMember user members - -deleteGroup :: !Group -> Task Group -deleteGroup group = dbDeleteItem (getItemId group) >>| return group - -addMemberToGroup :: !Group !User -> Task Group -addMemberToGroup group user - = dbReadItem (getItemId group) >>= \mbGroup -> case mbGroup of - Just group = dbUpdateItem {Group|group & members = removeDup (group.members ++ [user])} - Nothing = return group - -removeMemberFromGroup :: !Group !User -> Task Group -removeMemberFromGroup group user - = dbReadItem (getItemId group) >>= \mbGroup -> case mbGroup of - //If the current user is the last user, delete the group - Just group=:{members=[user]} = deleteGroup group - //Remove the user from the group - Just group=:{members} = dbUpdateItem {Group|group & members = removeMember user members} - Nothing = return group - -inviteUserToGroup :: !Group !User -> Task Group -inviteUserToGroup group user - = get currentUser - >>= \fromUser -> - appendTopLevelTask noMeta ( - user @: (invite fromUser group) - >>= \accept -> - if accept - (addMemberToGroup group user - >>= viewInformation ("Invitation accepted",toString user +++ " accepted your invitation to join the group " +++ toString group) [] - ) - (viewInformation ("Invitation declined",toString user +++ " declined your invitation to join the group " +++ toString group) [] group) - ) - >>| viewInformation ("Invitation sent","An invitation to join the group has been sent to " +++ toString user) [] group -where - invite user group - = viewInformation ( - "Invitation to join group " +++ toString group, - [Text (toString user +++ " invites you to join the group " +++ toString group +++ "."),BrTag [], Text "Do you accept this invitation?"]) - [] Void - >>+ \_ -> UserActions [(ActionNo,Just False),(ActionYes,Just True)] \ No newline at end of file diff --git a/Server/API/Extensions/Lists.dcl b/Server/API/Extensions/Lists.dcl deleted file mode 100644 index 130824e7fda6c5cc3d85fa80836f663d1c6d0842..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Lists.dcl +++ /dev/null @@ -1,64 +0,0 @@ -definition module Lists -/** -* This extension provides the ability to create lists -* (shopping lists, todo lists, meeting agendas etc) and -* share them with other users. -*/ -import iTasks - - -:: List a = - { listId :: !Hidden Int - , name :: !String - , description :: !Maybe Note - , items :: ![a] - } - -:: SimpleList :== List String -:: TodoList :== List (Bool, String) -:: DateList :== List (Date, String) -:: DocumentList :== List (Document, String) - -:: AnyList = SimpleList SimpleList - | TodoList TodoList - | DateList DateList - | DocumentList DocumentList - -derive class iTask List, AnyList - -/** -* Top level workflow for creating, viewing and sharing lists. -*/ -manageLists :: Task Void -/** -* Top level flow for a single list. -*/ -manageList :: !AnyList -> Task Void -/** -* Create a new list. -* -* @param type of list. Possible values "Simple list" "Todo list" "Date list" "Document list". -* @param A name -* @param An optional description -* -* @return The new list -*/ -createList :: !String !String !(Maybe Note) -> Task AnyList -/** -* Retrieve all lists stored in the system. -* -* @return The list of lists -*/ -getAllLists :: Task [AnyList] -/** -* Retrieve all lists that are created by, or shared with the current user. -* @return The list of lists -*/ -getMyLists :: Task [AnyList] -/** -* Delete a list -* -* @param The list to delete -* @return The deleted list -*/ -deleteList :: !AnyList -> Task AnyList \ No newline at end of file diff --git a/Server/API/Extensions/Lists.icl b/Server/API/Extensions/Lists.icl deleted file mode 100644 index ae448bb003b966105d8be0ecabdf4179322213eb..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Lists.icl +++ /dev/null @@ -1,199 +0,0 @@ -implementation module Lists - -import iTasks, Groups - -derive class iTask List, ListMeta, ListDescription, AnyList - -//Internal administration of who owns lists and who they are shared with. -:: ListMeta = - { listId :: !Int - , owner :: !User - , sharedWith :: ![User] - } - -//Data type for form name & description -:: ListDescription = - { name :: !String - , description :: !Maybe Note - } - -instance DB ListMeta -where - databaseId = sharedStore "Lists" [] - getItemId l = DBRef l.ListMeta.listId - setItemId (DBRef listId) l = {ListMeta| l & listId = listId} - -manageLists :: Task Void -manageLists - = Description "Manage lists" @>> - ( getMyLists - >>= overview - >>= \res -> case res of - (ActionOpen,Just list) = manageList list >>| return False - (ActionDelete,Just list) = delList list >>| return False - (ActionNew,_) = newList >>= manageList >>| return False - (ActionQuit,_) = return True - ) >| return Void -where - overview [] = viewInformation ("My lists","You have no lists.") [] Void >>+ \_ -> UserActions [(ActionNew,Just (ActionNew,Nothing)),(ActionQuit,Just (ActionQuit,Nothing))] - overview list = enterChoice ("My lists","Select a list...") [] list >>+ \{modelValue,localValid} -> let mbL = if localValid (Just modelValue) Nothing in UserActions [aOpen mbL,aDelete mbL,aNew,aQuit] - - aOpen mbL = (ActionOpen, maybe Nothing (\l -> Just (ActionOpen,Just l)) mbL) - aNew = (ActionNew, Just (ActionNew,Nothing)) - aQuit = (ActionQuit, Just (ActionQuit,Nothing)) - aDelete mbL = (ActionDelete, maybe Nothing (\l -> Just (ActionDelete,Just l)) mbL) - - newList = enterChoice ("List type","What type of list do you want to create?") [] - ["Simple list", "Todo list", "Date list","Document list"] - >>= \type -> - enterInformation ("Name","Please enter a name, and if you like, a description for the list") [] - >>= \desc -> - createList type desc.ListDescription.name desc.ListDescription.description - - delList list = viewInformation ("Delete list","Are you sure you want to delete '" +++ nameOf list +++ "'?") [] Void - >?* [ (ActionNo, Always (return list)) - , (ActionYes, Always (deleteList list)) - ] - -manageList :: !AnyList -> Task Void -manageList list - = - ( showItems list - >>= \action -> case action of - ActionEdit = editItems list >>| return False - Action "Share" = manageListSharing list >>| return False - ActionClose = return True - ) >| return Void -where - showItems l = case l of - (SimpleList l) = viewSharedInformation (l.List.name,l.List.description) [DisplayView (GetShared simpleFrom)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionClose,Just ActionClose),(ActionEdit,Just ActionEdit),(Action "Share",Just (Action "Share"))] - (TodoList l) = viewSharedInformation (l.List.name,l.List.description) [DisplayView (GetShared todoFrom)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionClose,Just ActionClose),(ActionEdit,Just ActionEdit),(Action "Share",Just (Action "Share"))] - (DateList l) = viewSharedInformation (l.List.name,l.List.description) [DisplayView (GetShared dateFrom)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionClose,Just ActionClose),(ActionEdit,Just ActionEdit),(Action "Share",Just (Action "Share"))] - (DocumentList l)= viewSharedInformation (l.List.name,l.List.description) [DisplayView (GetShared documentFrom)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionClose,Just ActionClose),(ActionEdit,Just ActionEdit),(Action "Share",Just (Action "Share"))] - - editItems list = case list of - (SimpleList l) = updateSharedInformation (l.List.name,l.List.description) [UpdateView (GetShared simpleFrom, SetShared simpleTo)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionFinish,Just Void)] - (TodoList l) = updateSharedInformation (l.List.name,l.List.description) [UpdateView (GetShared todoFrom, SetShared todoTo)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionFinish,Just Void)] - (DateList l) = updateSharedInformation (l.List.name,l.List.description) [UpdateView (GetShared dateFrom, SetShared dateTo)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionFinish,Just Void)] - (DocumentList l)= updateSharedInformation (l.List.name,l.List.description) [UpdateView (GetShared documentFrom, SetShared documentTo)] (sharedStore ("List-" <+++ (fromHidden l.List.listId)) defaultValue) Void >>+ \_ -> UserActions [(ActionFinish,Just Void)] - - simpleFrom (SimpleList l) = l.List.items - simpleTo i _ (SimpleList l) = SimpleList {List|l & items = i} - - todoFrom (TodoList l) = l.List.items - todoTo i _ (TodoList l) = TodoList {List|l & items = i} - - dateFrom (DateList l) = l.List.items - dateTo i _ (DateList l) = DateList {List|l & items = i} - - documentFrom (DocumentList l) = l.List.items - documentTo i _ (DocumentList l) = DocumentList {List|l & items = i} - - -manageListSharing :: AnyList -> Task Void -manageListSharing list - = - ( dbReadItem (DBRef (listIdOf list)) - >>= \mbMeta -> case mbMeta of - Nothing = throw "Could not find list meta data" - Just meta - = (case meta.ListMeta.sharedWith of - [] = viewInformation ("Sharing","This list is not shared") [] Void >>+ \_ -> UserActions [(aPrevious,Just (aPrevious,[])),(aAddPerson,Just (aAddPerson,[])),(aAddGroup,Just (aAddGroup,[]))] - users = enterMultipleChoice ("Sharing","This list is shared with the following people") [] users >>+ \{modelValue = users} -> UserActions [(aPrevious,Just (aPrevious,users)),(aRemove,Just (aRemove,users)),(aAddPerson,Just (aAddPerson,users)),(aAddGroup,Just (aAddGroup,users))] - ) - >>= \res -> case res of - (ActionDelete,users) = removeUsers users >>| return False - (Action "Add person(s)",_) = addUsers list >>| return False - (Action "Add group",_) = addGroup list >>| return False - (ActionPrevious,_) = return True - ) >| return Void - -where - aPrevious = ActionPrevious - aRemove = ActionDelete - aAddPerson = Action "Add person(s)" - aAddGroup = Action "Add group" - - removeUsers users = removeSharingForList list users - addUsers list = enterInformation ("Add person(s)","Enter the person(s) you want to share this list with") [] - >>= addSharingForList list - - addGroup list = getMyGroups - >>= \groups -> case groups of - [] = viewInformation ("Add group","You have no groups that you are member of") [] list - groups = enterChoice ("Add group","Which group do you want to share this list with?") [] groups - >>= \group -> - addSharingForList list group.members - -createList :: !String !String !(Maybe Note) -> Task AnyList -createList type name description - = storeMeta - >>= \meta -> - storeList meta.ListMeta.listId (makeList type name description meta.ListMeta.listId) -where - storeMeta :: Task ListMeta - storeMeta - = get currentUser - >>= \owner -> - dbCreateItem {ListMeta| listId = 0, owner = owner, sharedWith =[]} - - makeList :: !String !String !(Maybe Note) !Int -> AnyList - makeList "Todo list" name desc listId = TodoList {List|listId = (Hidden listId), name = name, description = desc, items = [] } - makeList "Date list" name desc listId = DateList {List|listId = (Hidden listId), name = name, description = desc, items = [] } - makeList "Document list" name desc listId = DocumentList {List|listId = (Hidden listId), name = name, description = desc, items = [] } - makeList _ name desc listId = SimpleList {List|listId = (Hidden listId), name = name, description = desc, items = [] } - - storeList :: !Int !AnyList -> Task AnyList - storeList listId list = set list (sharedStore ("List-" <+++ listId) defaultValue) - -getAllLists :: Task [AnyList] -getAllLists = dbReadAll >>= getLists - -getMyLists :: Task [AnyList] -getMyLists - = get currentUser >>= \user -> dbReadAll >>= transform (filter (hasAccess user)) >>= getLists -where - hasAccess user meta = user == meta.ListMeta.owner || isMember user meta.ListMeta.sharedWith - -getLists :: [ListMeta] -> Task [AnyList] -getLists [] = return [] -getLists meta = allTasks [get (sharedStore ("List-" <+++ m.ListMeta.listId) defaultValue) \\ m <- meta] - -deleteList :: !AnyList -> Task AnyList -deleteList list = return list/*deleteMeta listId >>| deleteList listId >>| return list -where - listId = listIdOf list - - deleteMeta :: Int -> Task (Maybe ListMeta) - deleteMeta listId = dbDeleteItem (DBRef listId) - - deleteList :: Int -> Task (Maybe AnyList) - deleteList listId = deleteShared (sharedStore ("List-" <+++ listId))*/ - -addSharingForList :: !AnyList ![User] -> Task AnyList -addSharingForList list users - = dbReadItem (DBRef (listIdOf list)) - >>= \mbMeta -> case mbMeta of - Nothing = throw "List meta data not found" - Just meta = dbUpdateItem {ListMeta| meta & sharedWith = meta.ListMeta.sharedWith ++ users} >>| return list - -removeSharingForList :: !AnyList ![User] -> Task AnyList -removeSharingForList list users - = dbReadItem (DBRef (listIdOf list)) - >>= \mbMeta -> case mbMeta of - Nothing = throw "List meta data not found" - Just meta = dbUpdateItem {ListMeta| meta & sharedWith = [u \\ u <- meta.ListMeta.sharedWith | not (isMember u users)]} >>| return list - -listIdOf :: !AnyList -> Int -listIdOf (SimpleList l) = fromHidden l.List.listId -listIdOf (TodoList l) = fromHidden l.List.listId -listIdOf (DateList l) = fromHidden l.List.listId -listIdOf (DocumentList l) = fromHidden l.List.listId - -nameOf :: !AnyList -> String -nameOf (SimpleList l) = l.List.name -nameOf (TodoList l) = l.List.name -nameOf (DateList l) = l.List.name -nameOf (DocumentList l) = l.List.name diff --git a/Server/API/Extensions/Messages.dcl b/Server/API/Extensions/Messages.dcl deleted file mode 100644 index d82b59365896eaa87b579471fadd1d86f2b43f48..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Messages.dcl +++ /dev/null @@ -1,76 +0,0 @@ -definition module Messages -/** -* This extension provides a simple e-mail-ish internal message system. -* If you receive messages they show up as tasks in your worklist. -* If you send messages you can choose to actively await a reply which will -* create a task in your worklist that is not completed until you receive a -* reply. -*/ - -import iTasks - -:: Message = - { messageId :: Hidden Int - , subject :: String - , sender :: Display User - , recipients :: [User] - , priority :: TaskPriority - , needsReply :: Bool - , message :: Note - , attachments :: Maybe [Document] - , thread :: Display [Message] - } - -/** -* Top level workflow for managing your received messages and starting -* point for writing new messages. -*/ -manageMessages :: Task Void -/** -* Top level flow for viewing a single message -* -* @param The message -* -* @return True if a reply has been sent -*/ -manageMessage :: !Message -> Task Bool -/** -* Combination of writing a new message followed by sending it. -*/ -newMessage :: Task Void -/** -* Combination of choosing a group to send a message to, -* writing the message and sending it. -*/ -newGroupMessage :: Task Void -/** -* Composition of a new message. -* -* @param The sender of the message -* @param The initial subject of the message -* @param If this message is a followup of another message, that message -* -* @param The new message (not stored in the database yet) -*/ -writeMessage :: User String [User] (Maybe Message) -> Task Message -/** -* Sending of a message. -* This stores the message to the database, making it available in "my messages" of -* the recipients. It also creates tasks for the recipients notifying them of the -* received message and if a reply is required creates the task for entering the reply. -* -* @param The message to be sent. -*/ -sendMessage :: Message -> Task Void -/** -* Retrieve all messages of which the current user is a recipient. -* -* @return The list of messages -*/ -getMyMessages :: Task [Message] -/** -* Retrieve all messages stored in the system. -* -* @return The list of messages -*/ -getAllMessages :: Task [Message] \ No newline at end of file diff --git a/Server/API/Extensions/Messages.icl b/Server/API/Extensions/Messages.icl deleted file mode 100644 index 114e511634fa57941886350d1cd83607dbb6c489..0000000000000000000000000000000000000000 --- a/Server/API/Extensions/Messages.icl +++ /dev/null @@ -1,156 +0,0 @@ -implementation module Messages - -import iTasks -import Groups - -derive class iTask Message - -:: Message = - { messageId :: Hidden Int - , subject :: String - , sender :: Display User - , recipients :: [User] - , priority :: TaskPriority - , needsReply :: Bool - , message :: Note - , attachments :: Maybe [Document] - , thread :: Display [Message] - } - -instance DB Message -where - databaseId = sharedStore "Messages" [] - getItemId m = DBRef (fromHidden m.Message.messageId) - setItemId (DBRef i) m = {Message|m & messageId = toHidden i} - -mkMsg :: User -> Message -mkMsg me = { Message - | messageId = toHidden 0 - , sender = toDisplay me - , subject = "New message" - , recipients = [] - , priority = NormalPriority - , needsReply = False - , message = Note "" - , attachments = Nothing - , thread = Display [] - } - -manageMessages :: Task Void -manageMessages = - ( getMyMessages - >>= overview - >>= \res -> case res of - (ActionOpen,Just message) = manageMessage message >>| return False - (Action "New message",_) = newMessage >>| return False - (Action "New group message",_) = newGroupMessage >>| return False - (ActionQuit,_) = return True - ) >| return Void -where - overview :: [Message] -> Task (Action,Maybe Message) - overview [] = viewInformation ("My messages","You have no messages") [] Void >>+ \_ -> UserActions [(aNew,Just (aNew,Nothing)),(aNewGroup,Just (aNewGroup,Nothing)),(aQuit,Just (aQuit,Nothing))] - overview msgs = enterChoice ("My messages","Your messages:") [] msgs >>+ \{modelValue,localValid} -> let mbM = if localValid (Just modelValue) Nothing in UserActions [(aOpen,maybe Nothing (\m -> Just (aOpen,Just m)) mbM),(aNew,Just (aNew,Nothing)),(aNewGroup,Just (aNewGroup,Nothing)),(aQuit,Just (aQuit,Nothing))] - - aOpen = ActionOpen - aNew = Action "New message" - aNewGroup = Action "New group message" - aQuit = ActionQuit - -manageMessage :: !Message -> Task Bool -manageMessage msg=:{Message |subject} - = viewInformation (subject,"You received a message") [About msg] Void >>+ (\_ -> UserActions [(aClose,Just aClose),(aReply,Just aReply),(aReplyAll,Just aReplyAll),(aForward,Just aForward),(aDelete,Just aDelete)]) - >>= \act -> case act of - ActionClose - = return False - (Action "Reply") - = get currentUser - >>= \me -> writeMessage me ("Re: " +++ msg.Message.subject) [(fromDisplay msg.sender)] (Just msg) - >>= \msg -> sendMessage msg - >>| return True - (Action "Reply All") - = get currentUser - >>= \me -> writeMessage me ("Re: " +++ msg.Message.subject) [(fromDisplay msg.sender):[u \\ u <- msg.recipients | u <> me]] (Just msg) - >>= \msg -> sendMessage msg - >>| return True - (Action "Forward") - = get currentUser - >>= \me -> writeMessage me ("Fw: " +++ msg.Message.subject) [] (Just msg) - >>= \msg -> sendMessage msg - >>| return False - ActionDelete - = dbDeleteItem (getItemId msg) - >>| viewInformation ("Deleted","Message deleted") [] False -where - aReply = Action "Reply" - aReplyAll = Action "Reply All" - aForward = Action "Forward" - aDelete = ActionDelete - aClose = ActionClose - -newMessage :: Task Void -newMessage - = get currentUser - >>= \me -> writeMessage me "" [] Nothing - >>= \msg -> sendMessage msg - -newGroupMessage :: Task Void -newGroupMessage = get currentUser - >>= \me -> getMyGroups - >>= \groups -> case groups of - [] = viewInformation ("No groups","You are not a member of any group") [] Void - _ = enterChoice ("Choose group","Select group") [] groups - >>= \group -> writeMessage me "" group.members Nothing - >>= \msg -> sendMessage msg - -sendMessage :: Message -> Task Void -sendMessage msg - = dbCreateItem msg - >>= \msg -> case msg.needsReply of - False = allTasks [appendTopLevelTask {noMeta & worker = Just rcp, priority = msg.Message.priority} (subject msg @>> manageMessage msg) \\ rcp <- msg.Message.recipients] >>| return Void - True = appendTopLevelTask noMeta (awaitReplies msg) >>| return Void - >>| viewInformation ("Message sent","The following message has been sent:") [About msg] Void -where - awaitReplies msg = - Description ("Waiting for reply on " +++ msg.Message.subject) @>> - case msg.Message.recipients of - [recipient] = recipient @: (askReplyTask recipient msg) >>= \answer -> notifyNoReplies [recipient] [answer] - recipients = allTasks [askReplyTask rcp msg \\ rcp <- recipients] >>= notifyNoReplies recipients - - askReplyTask user msg = - subject msg @>> - ((viewInformation ("Reply requested","The sender would like to receive a reply to this message.") [] Void >>+ noActions`) - ||- - manageMessage msg - ) - - subject msg - = Description ("Message from " +++ toString (fromDisplay msg.Message.sender)+++ ": "+++msg.Message.subject) - - notifyNoReplies recipients answers - = case [rcp \\ rcp <- recipients & ans <- answers | not ans] of - [] = return Void - users = viewInformation ("Reply request ignored","The following users ignored your request for a reply:") [About users] Void - - noActions` :: (TermFunc a Void) | iTask a - noActions` = noActions - -writeMessage :: User String [User] (Maybe Message) -> Task Message -writeMessage sender subj recipients mbThread - = updateInformation ("Compose","Enter your message") [] - {Message | (mkMsg sender) & subject = subj, recipients = recipients,thread = updateThread mbThread} -where - updateThread :: (Maybe Message) -> Display [Message] - updateThread Nothing = Display [] - updateThread (Just msg) = Display [{Message|msg & thread = Display []}:fromDisplay msg.Message.thread] - -getMyMessages :: Task [Message] -getMyMessages - = get currentUser - >>= \user -> - dbReadAll - >>= transform (filter (isRecipient user)) -where - isRecipient user msg = isMember user msg.Message.recipients - -getAllMessages ::Task [Message] -getAllMessages = dbReadAll diff --git a/Server/Framework/Config.dcl b/Server/Framework/Config.dcl deleted file mode 100644 index c673cafad3248401a08a6f2167a1041171e16016..0000000000000000000000000000000000000000 --- a/Server/Framework/Config.dcl +++ /dev/null @@ -1,34 +0,0 @@ -definition module Config -/** -* This module provides a configuration file for the iTasks server. -* An initial default configuration is written when no config file is found. -*/ -from Maybe import ::Maybe -from SystemTypes import :: Config - -/** -* Returns the default configuration -* -* @return Default configuration -*/ -defaultConfig :: Config -/** -* Load the config from disk. -* -* @param The application name -* @param The world -* -* @return The configuration data -* @return The world -*/ -loadConfig :: !String !*World -> (!Maybe Config, !*World) -/** -* Writes the config to disk -* -* @param The application name -* @param The configuration data -* @param The world -* -* @return The world -*/ -storeConfig :: !String !Config !*World -> *World \ No newline at end of file diff --git a/Server/Framework/Config.icl b/Server/Framework/Config.icl deleted file mode 100644 index 1a751b7a6383bfc190548adf42157733b3202d1e..0000000000000000000000000000000000000000 --- a/Server/Framework/Config.icl +++ /dev/null @@ -1,34 +0,0 @@ -implementation module Config - -import StdFile, Util, Error, File, FilePath, JSON, OS -import SystemTypes - - -defaultConfig :: Config -defaultConfig = - { clientPath = "Client" - , staticPath = ".\\Static" - , rootPassword = "root" - , rootEmail = "root@localhost" - , sessionTime = 3600 - , serverPort = 80 - , serverPath = "/services" - , debug = False - , smtpServer = "localhost" - , generalWorkflows = False - , runAsyncPath = ".." "Tools" "RunAsync" IF_POSIX_OR_WINDOWS "RunAsync" "RunAsync.exe" - , curlPath = IF_POSIX_OR_WINDOWS "/usr/bin/curl" "..\\Tools\\Curl\\curl.exe" - } - - -loadConfig :: !String !*World -> (!Maybe Config, !*World) -loadConfig appName world - # (res,world) = readFile (appName +++ "-config.json") world - | isError res = (Nothing, world) - = (fromJSON (fromString (fromOk res)),world) - -storeConfig :: !String !Config !*World -> *World -storeConfig appName config world - # (_, world) = writeFile (appName +++ "-config.json") (toString (toJSON config)) world - = world - \ No newline at end of file diff --git a/Server/Framework/Engine.dcl b/Server/Framework/Engine.dcl index d39be6b5e76a09f535979c8f821bc46430f26a48..dafbfaf1289c9e8462dfe75772f6fb854fe63020 100644 --- a/Server/Framework/Engine.dcl +++ b/Server/Framework/Engine.dcl @@ -4,10 +4,9 @@ definition module Engine * This is the primary function that creates the complete * environment in which worfklow specifications can be executed. */ -import Maybe, JSON, Task +import Maybe, JSON, FilePath, Task from IWorld import :: IWorld from HTTP import :: HTTPRequest, :: HTTPResponse -from Config import :: Config :: PublishedTask = { url :: String @@ -25,10 +24,10 @@ from Config import :: Config /** * Creates the iTasks system from a set of published tasks * -* @param An optional config record +* @param The config record * @param A task to execute */ -engine :: !(Maybe Config) publish -> [(!String -> Bool,!HTTPRequest *World -> (!HTTPResponse, !*World))] | Publishable publish +engine :: !FilePath publish -> [(!String -> Bool,!HTTPRequest *World -> (!HTTPResponse, !*World))] | Publishable publish /** * Wraps a task together with a url to make it publishable by the engine @@ -43,21 +42,17 @@ instance Publishable (Task a) | iTask a instance Publishable [PublishedTask] /** -* Loads the itasks specific config -* -* @param The world -* -* @return The configuration options -* @return The updated world +* Determines the server executables path */ -config :: !*World -> (!Maybe Config,!*World) +determineAppPath :: !*World -> (!FilePath, !*World) /** -* Determines the server executables path +* Determine the name of the application based on the executable's name */ -determineAppPath :: !*World -> (!String, !*World) +determineAppName :: !*World -> (!String,!*World) /** -* Determine the name of the application based on the executable's name +* Determine the location of the iTasks SDK */ -determineAppName :: !*World -> (!String,!*World) \ No newline at end of file +determineSDKPath :: ![FilePath] !*World -> (!Maybe FilePath, !*World) + diff --git a/Server/Framework/Engine.icl b/Server/Framework/Engine.icl index ef8032c9aed96aa3896fa98ef819336fcd1fdf7a..9e33f23bf2ae65d9e1462c16bbb6c12a2133f018 100644 --- a/Server/Framework/Engine.icl +++ b/Server/Framework/Engine.icl @@ -4,37 +4,27 @@ import StdMisc, StdArray, StdList, StdOrdList, StdTuple, StdChar, StdFile, StdBo from StdFunc import o, seqList, ::St import Map, Time, CommandLine, Error, File, FilePath, Directory, HTTP, OSError, Text, MIME, UrlEncoding import Util, HtmlUtil -import TuningCombinators -import Setup -import Config import IWorld import WebService // The iTasks engine consist of a set of HTTP request handlers -engine :: !(Maybe Config) publish -> [(!String -> Bool,!HTTPRequest *World -> (!HTTPResponse, !*World))] | Publishable publish -engine mbConfig publishable - = case mbConfig of - Just config - = handlers` config - Nothing - = [(\_ -> True, setupHandler)] +engine :: !FilePath publish -> [(!String -> Bool,!HTTPRequest *World -> (!HTTPResponse, !*World))] | Publishable publish +engine sdkPath publishable + = taskHandlers (publishAll publishable) sdkPath ++ defaultHandlers sdkPath where - handlers` config - = taskHandlers (publishAll publishable) config ++ defaultHandlers config - - taskHandlers published config - = [((==) url, taskDispatch config task defaultFormat) \\ {url,task=TaskWrapper task,defaultFormat} <- published] + taskHandlers published sdkPath + = [((==) url, taskDispatch sdkPath task defaultFormat) \\ {url,task=TaskWrapper task,defaultFormat} <- published] - taskDispatch config task defaultFormat req world - # iworld = initIWorld config world + taskDispatch sdkPath task defaultFormat req world + # iworld = initIWorld sdkPath world # (response,iworld) = webService task defaultFormat req iworld = (response, finalizeIWorld iworld) - defaultHandlers config - = [((==) "/stop", handleStopRequest),(\_ -> True, handleStaticResourceRequest config)] + defaultHandlers sdkPath + = [((==) "/stop", handleStopRequest),(\_ -> True, handleStaticResourceRequest sdkPath)] -initIWorld :: !Config !*World -> *IWorld -initIWorld config world +initIWorld :: !FilePath !*World -> *IWorld +initIWorld sdkPath world # (appName,world) = determineAppName world # (appPath,world) = determineAppPath world # appDir = takeDirectory appPath @@ -53,7 +43,8 @@ initIWorld config world |application = appName ,storeDirectory = storePath ,tmpDirectory = tmpPath - ,config = config + ,sdkDirectory = sdkPath + ,config = defaultConfig ,timestamp = timestamp ,latestEvent = Nothing ,localDateTime = localDateTime @@ -64,7 +55,15 @@ initIWorld config world ,readShares = Nothing ,world = world } -where +where + defaultConfig :: Config + defaultConfig = + { rootPassword = "root" + , rootEmail = "root@localhost" + , sessionTime = 3600 + , smtpServer = "localhost" + } + padZero :: !Int -> String padZero number = (if (number < 10) "0" "") +++ toString number @@ -82,17 +81,17 @@ finalizeIWorld iworld=:{IWorld|world} = world // Request handler which serves static resources from the application directory, // or a system wide default directory if it is not found locally. // This request handler is used for serving system wide javascript, css, images, etc... -handleStaticResourceRequest :: !Config !HTTPRequest *World -> (!HTTPResponse,!*World) -handleStaticResourceRequest config req world +handleStaticResourceRequest :: !FilePath !HTTPRequest *World -> (!HTTPResponse,!*World) +handleStaticResourceRequest sdkPath req world # path = if (req.req_path == "/") "/index.html" req.req_path - # filename = config.clientPath +++ filePath path + # filename = sdkPath "Client" filePath path # type = mimeType filename # (mbContent, world) = readFile filename world | isOk mbContent = ({rsp_headers = fromList [("Status","200 OK"), ("Content-Type", type), ("Content-Length", toString (size (fromOk mbContent)))] ,rsp_data = fromOk mbContent}, world) - # filename = config.staticPath +++ filePath path + # filename = sdkPath "Static" filePath path # type = mimeType filename # (mbContent, world) = readFile filename world | isOk mbContent = ({rsp_headers = fromList [("Status","200 OK"), @@ -122,13 +121,8 @@ instance Publishable [PublishedTask] where publishAll list = list -config :: !*World -> (!Maybe Config,!*World) -config world - # (appName,world) = determineAppName world - = loadConfig appName world - // Determines the server executables path -determineAppPath :: !*World -> (!String, !*World) +determineAppPath :: !*World -> (!FilePath, !*World) determineAppPath world # ([arg:_],world) = getCommandLine world | dropDirectory arg <> "ConsoleClient.exe" = (arg, world) @@ -152,3 +146,15 @@ determineAppName :: !*World -> (!String,!*World) determineAppName world # (appPath, world) = determineAppPath world = ((dropExtension o dropDirectory) appPath, world) + +determineSDKPath :: ![FilePath] !*World -> (!Maybe FilePath, !*World) +determineSDKPath [] world = (Nothing, world) +determineSDKPath [p:ps] world + # (mbInfo,world) = getFileInfo path world + = case mbInfo of + Ok info | info.directory = (Just path,world) + _ = determineSDKPath ps world +where + path = (p "iTasks-SDK") + + \ No newline at end of file diff --git a/Server/Framework/EngineWrapperStandalone.icl b/Server/Framework/EngineWrapperStandalone.icl index 6d73514d903bf1ad3ef738a51a20720e203b321b..00ef584acb43b75da642000ff867b0b13658f4c3 100644 --- a/Server/Framework/EngineWrapperStandalone.icl +++ b/Server/Framework/EngineWrapperStandalone.icl @@ -1,46 +1,90 @@ implementation module EngineWrapperStandalone -import StdFile, StdInt -import Engine, Config -import HTTP, HttpServer +import StdFile, StdInt, StdList, StdChar, StdBool, StdString +import HTTP, HttpServer, CommandLine, Func -import WebService, DocumentService +import Engine + +DEFAULT_PORT :== 80 +SEARCH_PATHS :== [".","..",".." "..",".." ".." "..","C:\\Clean2.3"] startEngine :: a !*World -> *World | Publishable a startEngine publishable world - # (mbConfig,world) = config world - # (app,world) = determineAppName world - # world = instructions app mbConfig world - # options = case mbConfig of - Just config = [HTTPServerOptPort config.serverPort, HTTPServerOptDebug config.debug] - Nothing = [] - # world = http_startServer options (engine mbConfig publishable) world - | isJust mbConfig - = world // normal operation: stop server - | otherwise - # (console,world) = stdio world - # console = fwrites ("\n\n") console - # (_,world) = fclose console world - = startEngine publishable world // setup mode: restart server + # (opts,world) = getCommandLine world + # (app,world) = determineAppName world + # (mbSDKPath,world) = determineSDKPath SEARCH_PATHS world + // Show server name + # world = show (infoline app) world + //Check options + # port = fromMaybe DEFAULT_PORT (intOpt "-p" opts) + # debug = boolOpt "-d" opts + # help = boolOpt "-h" opts + # sdkOpt = stringOpt "-s" opts + //If -h option is given show help and stop + | help = show instructions world + //Check sdkpath + # mbSDKPath = maybe mbSDKPath Just sdkOpt //Commandline SDK option overrides found paths + | isNothing mbSDKPath = show sdkpatherror world + //Normal execution + # world = show (running port) world + # options = [HTTPServerOptPort port, HTTPServerOptDebug debug] + # world = http_startServer options (engine (fromJust mbSDKPath) publishable) world + = world where - instructions :: !String !(Maybe Config) *World -> *World - //Normal operation - instructions app (Just config=:{serverPort,serverPath,staticPath,clientPath}) world + infoline :: !String -> [String] + infoline app = ["*** " +++ app +++ " HTTP server ***",""] + + instructions :: [String] + instructions = + ["Available commandline options:" + ," -h : Show this message and exit" + ," -s : Use as location of the iTasks SDK" + ," -p : Set port number (default " +++ toString DEFAULT_PORT +++ ")" + ," -d : Run server in debug mode" + ,"" + ] + + sdkpatherror :: [String] + sdkpatherror = + ["Oops! Could not find the iTasks SDK." + ,"The server needs to know the location of the SDK to serve static content" + ,"and run its various utility programs." + ,"" + ,"Please put the \"iTasks-SDK\" folder in one of the search locations" + ,"or use the -s commandline flag to set the path." + ,"Example: -d C:\\Users\\johndoe\\Desktop\\Clean2.3\\iTasks-SDK" + ,"" + ,"Tried to find a folder named \"iTasks-SDK\" in the following search locations:" + :SEARCH_PATHS] + + running :: !Int -> [String] + running port = ["Running at http://localhost" +++ (if (port == 80) "/" (":" +++ toString port +++ "/"))] + + show :: ![String] !*World -> *World + show lines world # (console,world) = stdio world - # console = fwrites ("*** " +++ app +++ " HTTP server started ***\n\n") console - # console = fwrites ("Serving client from directory: " +++ clientPath +++ "\n") console - # console = fwrites ("Serving static content from directory: " +++ staticPath +++ "\n\n") console - # console = fwrites ("You can access the client at: " +++ host +++ "/\n") console - # console = fwrites ("You can access the services directly at: " +++ host +++ serverPath +++ "\n") console + # console = seqSt (\s c -> fwrites (s +++ "\n") c) lines console # (_,world) = fclose console world = world - where - host = if (serverPort == 80) "http://localhost" ("http://localhost:" +++ toString serverPort) - //Setup mode - instructions app Nothing world - # (console,world) = stdio world - # console = fwrites ("*** " +++ app +++ " HTTP server started in setup mode***\n\n") console - # console = fwrites ("Please open http://localhost/ and follow instructions\n") console - # (_,world) = fclose console world - = world - \ No newline at end of file + + boolOpt :: !String ![String] -> Bool + boolOpt key opts = isMember key opts + + intOpt :: !String ![String] -> Maybe Int + intOpt key [] = Nothing + intOpt key [_] = Nothing + intOpt key [n,v:r] + | n == key && isInteger v = Just (toInt v) + = intOpt key [v:r] + where + isInteger v = and (map isDigit (fromString v)) + + stringOpt :: !String [String] -> Maybe String + stringOpt key [] = Nothing + stringOpt key [_] = Nothing + stringOpt key [n,v:r] + | n == key = Just v + = stringOpt key [v:r] + + + \ No newline at end of file diff --git a/Server/Framework/HtmlUtil.dcl b/Server/Framework/HtmlUtil.dcl index 59e9595c9d2746443903148417593a1f51bb77d1..6ff3eb7b5b487d4a58b6edc0133530115d12bfed 100644 --- a/Server/Framework/HtmlUtil.dcl +++ b/Server/Framework/HtmlUtil.dcl @@ -4,44 +4,8 @@ definition module HtmlUtil * */ import HTML, JSON, HTTP - -/** -* Defines the simple inline CSS stylesheet -*/ -embeddedStyle :: HtmlTag -/** -* Creates the basic layout for a page with a title, description and body -*/ -pageLayout :: !String !String ![HtmlTag] -> HtmlTag - -/** -* Creates a simple page for accessing a service -*/ -servicePage :: !String !String !String ![(String,String,Bool)] !JSONNode -> HtmlTag - -/** -* Creates an HTTP response of a service page -*/ -serviceResponse :: !Bool !String !String !String ![(String,String,Bool)] !JSONNode -> HTTPResponse - -/** -* Creates a start page to load the client framework -*/ -appStartPage :: !String -> HtmlTag -/** -* Creates an HTTP response of the start page -*/ -appStartResponse :: !String -> HTTPResponse -/** -* Creates a 302 Redirect response -*/ -redirectResponse :: !String -> HTTPResponse -/** -* Creates a 404 Not found error page -*/ -notFoundPage :: !HTTPRequest -> HtmlTag -/** -* Creates an HTTP 404 response +/* +* Generate a 404 page */ notFoundResponse :: !HTTPRequest -> HTTPResponse /** diff --git a/Server/Framework/HtmlUtil.icl b/Server/Framework/HtmlUtil.icl index 9ac3ade8cd122ac44ed0767a93c0ae421cbce88c..fcdcad8e4f0b4710600c4b4b85c981846f2add64 100644 --- a/Server/Framework/HtmlUtil.icl +++ b/Server/Framework/HtmlUtil.icl @@ -1,6 +1,6 @@ implementation module HtmlUtil -import HTML, JSON, Text, HTTP, Map +import HTML, JSON, Text, HTTP, Map, OS import StdList, StdBool embeddedStyle :: HtmlTag @@ -29,63 +29,6 @@ where header = [H1Tag [] [Text title],PTag [] [DivTag [ClassAttr "description"] [Html description]]] - -servicePage :: !String !String !String ![(String,String,Bool)] !JSONNode -> HtmlTag -servicePage title description url params json = pageLayout title description [parameters, message, alternatives] -where - parameters = pageSection "Parameters" [FormTag [ActionAttr url,MethodAttr "get"] [TableTag [ClassAttr "parameters"] (rows ++ send)]] - rows = [TrTag [] [ThTag [] [Text n : if o [Text "*:"] [Text ":"]], TdTag [] [InputTag [NameAttr n, ValueAttr v]]] \\ (n,v,o) <- params] - send = [TrTag [] [TdTag [ColspanAttr "4"] [ButtonTag [TypeAttr "submit"] [Text "Send"]]]] - message = pageSection "Data" [DivTag [ClassAttr "json"] (formatJSON json)] - jsonurl = replaceSubString "services/html" "services/json" url - alternatives= pageSection "Alternative representations" [PTag [] [Text "JSON: ", ATag [HrefAttr jsonurl] [Text jsonurl]]] - -serviceResponse :: !Bool !String !String !String ![(String,String,Bool)] !JSONNode -> HTTPResponse -serviceResponse html title description url params json = - if html {newHTTPResponse & rsp_data = toString (servicePage title description url params json)} - { newHTTPResponse - //Content-Type for JSON should be "application/json", see http://www.ietf.org/rfc/rfc4627.txt - & rsp_headers = put "Content-Type" "application/json" (newHTTPResponse.rsp_headers) - , rsp_data = toString json - } - - -formatJSON :: !JSONNode -> [HtmlTag] -formatJSON (JSONNull) = [Text "null"] -formatJSON (JSONBool True) = [Text "true"] -formatJSON (JSONBool False) = [Text "false"] -formatJSON (JSONInt i) = [Text (toString i)] -formatJSON (JSONReal r) = [Text (toString r)] -formatJSON (JSONString s) = [Text "\"", Text s, Text "\""] -formatJSON (JSONArray items) = [UlTag [] [LiTag [] (formatJSON node) \\ node <- items] ] -formatJSON (JSONObject fields) = [UlTag [] [LiTag [] [Text label,Text ": " :formatJSON node] \\(label,node) <- fields ] ] -formatJSON (JSONRaw r) = [PreTag [] [Text (toString r)]] -formatJSON _ = [] - -appStartPage :: !String -> HtmlTag -appStartPage appName = HtmlTag [] [head,body] -where - head = HeadTag [] [TitleTag [] [Text "Loading..."]: styles ++ scripts] - body = BodyTag [] [] - - styles = [LinkTag [RelAttr "stylesheet", HrefAttr file, TypeAttr "text/css"] [] \\ file <- stylefiles] - scripts = [ScriptTag [SrcAttr file, TypeAttr "text/javascript"] [] \\ file <- scriptfiles] - - stylefiles = ["/lib/ext-4.0.2a/resources/css/ext-all-gray.css" - ,"/src/css/main.css" - ,appName +++ ".css"] - scriptfiles = ["/lib/ext-4.0.2a/ext-debug.js","/src/app.js"] - -/** -* Creates an HTTP response of the start page -*/ -appStartResponse :: !String -> HTTPResponse -appStartResponse appName = {newHTTPResponse & rsp_data = toString (appStartPage appName)} - -redirectResponse :: !String -> HTTPResponse -redirectResponse url - = {HTTPResponse | rsp_headers = fromList [("Status","302 - Found"),("Location",url)], rsp_data = ""} - notFoundPage :: !HTTPRequest -> HtmlTag notFoundPage req = pageLayout "404 - Not Found" "" message where @@ -95,9 +38,6 @@ notFoundResponse :: !HTTPRequest -> HTTPResponse notFoundResponse req = {HTTPResponse | rsp_headers = fromList [("Status","404 - Not Found")], rsp_data = toString (notFoundPage req)} -pageSection :: !String ![HtmlTag] -> HtmlTag -pageSection title content = DivTag [ClassAttr "section"] [H2Tag [] [Text title]:content] - paramValue :: !String !HTTPRequest -> String paramValue name req = case get name req.arg_post of @@ -106,18 +46,16 @@ paramValue name req Just val = val Nothing = "" -NEWLINE :== "\n" - nl2br :: !String -> HtmlTag -nl2br str = html [[Text line,BrTag []] \\ line <- split NEWLINE str] +nl2br str = html [[Text line,BrTag []] \\ line <- split OS_NEWLINE str] html2text :: !String -> String html2text s - # s = replaceSubString "
" NEWLINE s - # s = replaceSubString "
" NEWLINE s - # s = replaceSubString "
" NEWLINE s - # s = replaceSubString "
" NEWLINE s - # s = replaceSubString "" NEWLINE s + # s = replaceSubString "
" OS_NEWLINE s + # s = replaceSubString "
" OS_NEWLINE s + # s = replaceSubString "
" OS_NEWLINE s + # s = replaceSubString "
" OS_NEWLINE s + # s = replaceSubString "" OS_NEWLINE s # s = stripHtmlTags s # s = replaceSubString " " " " s # s = replaceSubString "<" "<" s diff --git a/Server/Framework/IWorld.dcl b/Server/Framework/IWorld.dcl index 1870d9d4e8d88232759727ca74d313c1e1f750d2..61f7d8f957a417c7d3ecc7cccb5ba1235aaed328 100644 --- a/Server/Framework/IWorld.dcl +++ b/Server/Framework/IWorld.dcl @@ -1,16 +1,15 @@ definition module IWorld -from Config import :: Config from FilePath import :: FilePath from Map import :: Map from Maybe import :: Maybe -from SystemTypes import :: DateTime, :: User +from SystemTypes import :: DateTime, :: User, :: ProcessId, :: Config from Time import :: Timestamp -from SystemTypes import :: ProcessId :: *IWorld = { application :: !String // The name of the application , storeDirectory :: !FilePath // The generic data store , tmpDirectory :: !FilePath // The path for temporary files, the garbage collector also works on files in this dir + , sdkDirectory :: !FilePath // Location of the iTasks SDK , config :: !Config // The server configuration , timestamp :: !Timestamp // The timestamp of the current request , localDateTime :: !DateTime // The local date & time of the current request diff --git a/Server/Framework/IWorld.icl b/Server/Framework/IWorld.icl index cb6ec36409b70c5c3800a156cd787dc0ef8f4edf..ac75e5a5ca2adad8782abd68f9aecee2c48362d5 100644 --- a/Server/Framework/IWorld.icl +++ b/Server/Framework/IWorld.icl @@ -1,8 +1,7 @@ implementation module IWorld -from Config import :: Config from FilePath import :: FilePath from Map import :: Map from Maybe import :: Maybe -from SystemTypes import :: DateTime, :: User, :: ProcessId +from SystemTypes import :: DateTime, :: User, :: ProcessId, :: Config from Time import :: Timestamp \ No newline at end of file diff --git a/Server/Framework/Setup.dcl b/Server/Framework/Setup.dcl deleted file mode 100644 index 5d73cd2ff938c4a706b5afc1f07e2074f33f704a..0000000000000000000000000000000000000000 --- a/Server/Framework/Setup.dcl +++ /dev/null @@ -1,5 +0,0 @@ -definition module Setup - -import HTTP, Config - -setupHandler :: !HTTPRequest !*World -> (!HTTPResponse, !*World) \ No newline at end of file diff --git a/Server/Framework/Setup.icl b/Server/Framework/Setup.icl deleted file mode 100644 index 63ddb81eccd490c7481b5f72f961941b62a225d5..0000000000000000000000000000000000000000 --- a/Server/Framework/Setup.icl +++ /dev/null @@ -1,209 +0,0 @@ -implementation module Setup - -import StdList,StdBool, StdInt, StdFile, StdFunc -import HTTP, Map -import HTML, HtmlUtil -import File, Error, OS -import Config -import Engine, Util - -setupHandler :: !HTTPRequest !*World -> (!HTTPResponse, !*World) -setupHandler req world - # (appName,world) = determineAppName world - # (config,world) = if (isEmpty (toList req.arg_post)) (initialConfig world) (postedConfig req, world) - # (errors,world) = checkConfig config world - = case req.req_path of - "/edit" = editConfigPage appName config errors world - "/root" - | noErrors errors = rootPasswordPage appName config world - | otherwise = editConfigPage appName config errors world - "/save" = saveConfigPage appName config world - _ = choicePage appName config errors world - -//Initial config of the form -initialConfig :: !*World -> (!Config,!*World) -initialConfig world - # (clientPath,world) = findClientPath 10 "Client" world - # (runAsyncPath,world) = findPath 10 ("Tools" "RunAsync" IF_POSIX_OR_WINDOWS "RunAsync" "RunAsync.exe") - defaultConfig.runAsyncPath world - # (curlPath,world) = findPath 10 ("Tools" "Curl" (IF_POSIX_OR_WINDOWS "curl" "curl.exe")) - defaultConfig.curlPath world - = ({defaultConfig & clientPath = clientPath, runAsyncPath = runAsyncPath, curlPath = curlPath},world) -where - findClientPath 0 path world = (".",world) - findClientPath i path world - # (ok,world) = checkClientPath path world - | ok = (path,world) - //# buildpath = path +++ "\\build" - //# (ok,world) = checkClientPath buildpath world - //| ok = (buildpath,world) - = findClientPath (dec i) ("..\\" +++ path) world - - findPath 0 path defaultPath world = (defaultPath, world) - findPath i path defaultPath world - # (ok,world) = fileExists path world - | ok = (path,world) - = findPath (dec i) (".." path) defaultPath world - -postedConfig :: !HTTPRequest -> Config -postedConfig req = - { clientPath = fromMaybe "" (get "clientPath" req.arg_post) - , staticPath = fromMaybe "" (get "staticPath" req.arg_post) - , rootPassword = fromMaybe "" (get "rootPassword" req.arg_post) - , rootEmail = fromMaybe "" (get "rootEmail" req.arg_post) - , sessionTime = maybe 0 toInt (get "sessionTime" req.arg_post) - , serverPort = maybe 0 toInt (get "serverPort" req.arg_post) - , serverPath = fromMaybe "" (get "serverPath" req.arg_post) - , debug = (fromMaybe "false" (get "debug" req.arg_post)) <> "false" - , smtpServer = fromMaybe "" (get "smtpServer" req.arg_post) - , generalWorkflows = (fromMaybe "false" (get "generalWorkflows" req.arg_post)) <> "false" - , runAsyncPath = fromMaybe "" (get "runAsyncPath" req.arg_post) - , curlPath = fromMaybe "" (get "curlPath" req.arg_post) - } - -checkConfig :: !Config !*World -> (![Maybe String],!*World) -checkConfig config world - # (clientPathOk,world) = checkClientPath config.clientPath world - # (runAsyncOk,world) = fileExists config.runAsyncPath world - # (curlOk,world) = fileExists config.curlPath world - = ([if clientPathOk Nothing (Just CLIENT_ERROR) - ,Nothing - ,if (config.sessionTime < 60) (Just "Session time should be at least 60 seconds") Nothing - ,if ((config.serverPort < 0) || (config.serverPort > 60000)) (Just "Server port should be between 1 and 60000") Nothing - ,Nothing - ,Nothing - ,Nothing - ,Nothing - ,if runAsyncOk Nothing (Just RUNASYNC_ERROR) - ,if curlOk Nothing (Just CURL_ERROR) - ],world) - -CLIENT_ERROR :== "The client framework could not be found at this location.
" - +++ "Please fill in the full path where the client framework can be found.
" - +++ "It can normally be found in the \"Client\" folder of the SDK. For example C:\\iTasks-SDK\\Client." - -RUNASYNC_ERROR :== "The RunAsync tool could not be found at this location.
" - +++ "Please fill in the full path where the RunAsync tool can be found.
" - +++ "It can be compiled from the module RunAsync.icl, which can be found in the \"Tools\\RunAsync\" folder of the SDK. " - -CURL_ERROR :== "The Curl tool could not be found at this location.
" - +++ "Please fill in the full path where the Curl tool can be found.
" - -checkClientPath :: !String !*World -> (!Bool,!*World) -checkClientPath clientPath world - # (res,world) = readFile (clientPath +++ "\\src\\app.js") world - = (isOk res,world) - -configFileAvailable :: !String !*World -> (!Bool,!*World) -configFileAvailable appName world - # (res,world) = readFile (appName +++ "-config.json") world - = (isOk res, world) - -noErrors :: [(Maybe String)] -> Bool -noErrors errors = not (or (map isJust errors)) - -page :: !String ![HtmlTag] !*World -> (!HTTPResponse, !*World) -page appName content world = ({newHTTPResponse & rsp_data = toString (pageLayout (appName +++ " setup") "" content)}, world) - -choicePage :: !String !Config ![Maybe String] !*World -> (!HTTPResponse,!*World) -choicePage appName config errors world = page appName [DivTag [IdAttr "content"] [instructions,showConfig config errors],buttons] world -where - instructions - = PTag [] - [Text "Welcome, you are running ",StrongTag [] [Text appName],Text " for the first time.", BrTag[] - ,Text "You may run this application with the following default configuration, or edit it first." - ] - buttons = DivTag [ClassAttr "buttons"] - [ButtonTag [TypeAttr "submit",OnclickAttr "window.location = '/root';"] [Text "Use this default configuration"] - ,ButtonTag [TypeAttr "submit",OnclickAttr "window.location = '/edit';"] [Text "Edit the configuration first"] - ] - -editConfigPage :: !String !Config ![Maybe String] !*World -> (!HTTPResponse,!*World) -editConfigPage appName config errors world = page appName [form] world -where - form = FormTag [MethodAttr "post",ActionAttr "/root"] [DivTag [IdAttr "content"] (editConfig config errors),submit] - submit = DivTag [ClassAttr "buttons"] [ButtonTag [TypeAttr "submit"] [Text "Save configuration"]] - - instructions - = PTag [] [Text "Please confirm the configuration settings below and save them."] - -rootPasswordPage :: !String !Config !*World -> (!HTTPResponse,!*World) -rootPasswordPage appName config world = page appName [form] world -where - form = FormTag [MethodAttr "post",ActionAttr "/save"] [DivTag [IdAttr "content"] (editRoot config),submit] - submit = DivTag [ClassAttr "buttons"] [ButtonTag [TypeAttr "submit"] [Text "Save configuration"]] - - instructions - = PTag [] [Text "Please confirm the root e-mail address and password."] - -saveConfigPage :: !String !Config !*World -> (!HTTPResponse,!*World) -saveConfigPage appName config world - # world = storeConfig appName config world - = ({newHTTPResponse & rsp_headers = fromList [("X-Server-Control","stop")], rsp_data = toString (pageLayout (appName +++ " setup") "" content)}, world) -where - content = [ DivTag [IdAttr "content"] - [ Text "The configuration file has been written.", BrTag [] - , Text "You can now run ", StrongTag [] [Text appName], Text ".", BrTag [] - ] - , DivTag [ClassAttr "buttons"] [ButtonTag [TypeAttr "submit",OnclickAttr ("window.location = '" +++ redirectUrl +++ "';")] [Text ("Run " +++ appName)]] - ] - redirectUrl = if (config.serverPort == 80) "http://localhost/" ("http://localhost:" +++ toString config.serverPort +++ "/") - -showConfig :: Config [Maybe String] -> HtmlTag -showConfig config errors = TableTag [] - [TrTag [ClassAttr (errclass error)] [ThTag [] [Text label,Text":"],TdTag [] [Text setting],TdTag [] (errmsg error) ] \\ (label,setting) <- fields & error <- errors] -where - fields = [("Client path", config.clientPath) - ,("Static path", config.staticPath) - ,("Session time", toString config.sessionTime) - ,("Server port", toString config.serverPort) - ,("Server path", config.serverPath) - ,("Debug", toString config.debug) - ,("Smtp server", config.smtpServer) - ,("Enable general workflows", toString config.generalWorkflows) - ,("RunAsync path", config.runAsyncPath) - ,("Curl path", config.curlPath) - ] -editConfig :: !Config ![Maybe String] -> [HtmlTag] -editConfig config errors = [TableTag [] - [TrTag [ClassAttr (errclass error)] [ThTag [] [Text label,Text":"],TdTag [] [input],TdTag [] (errmsg error)] \\ (label,input) <- fields & error <- errors] - : hidden] -where - fields = [("Client path",InputTag [TypeAttr "text",NameAttr "clientPath", ValueAttr config.clientPath]) - ,("Static path",InputTag [TypeAttr "text",NameAttr "staticPath", ValueAttr config.staticPath]) - ,("Session time",InputTag [TypeAttr "text",NameAttr "sessionTime",SizeAttr "2", ValueAttr (toString config.sessionTime)]) - ,("Server port",InputTag [TypeAttr "text",NameAttr "serverPort",SizeAttr "2", ValueAttr (toString config.serverPort)]) - ,("Server path",InputTag [TypeAttr "text",NameAttr "serverPath", ValueAttr config.serverPath]) - ,("Debug",InputTag [TypeAttr "checkbox",NameAttr "debug":if config.debug [CheckedAttr] [] ]) - ,("Smtp server",InputTag [TypeAttr "text",NameAttr "smtpServer", ValueAttr config.smtpServer]) - ,("Enable general workflows",InputTag [TypeAttr "checkbox",NameAttr "generalWorkflows":if config.generalWorkflows [CheckedAttr] [] ]) - ,("RunAsync path",InputTag [TypeAttr "text",NameAttr "runAsyncPath", ValueAttr config.runAsyncPath]) - ,("Curl path",InputTag [TypeAttr "text",NameAttr "curlPath", ValueAttr config.curlPath]) - ] - hidden = [ InputTag [TypeAttr "hidden",NameAttr "rootPassword", ValueAttr config.rootPassword] - , InputTag [TypeAttr "hidden",NameAttr "rootEmail", ValueAttr config.rootEmail] - ] - -editRoot :: !Config -> [HtmlTag] -editRoot config = [TableTag [] - [TrTag [ClassAttr (errclass Nothing)] [ThTag [] [Text label,Text":"],TdTag [] [input]] \\ (label,input) <- fields] - : hidden] -where - fields = [ ("Root password",InputTag [TypeAttr "text",NameAttr "rootPassword", ValueAttr config.rootPassword]) - , ("Root e-mail",InputTag [TypeAttr "text",NameAttr "rootEmail", ValueAttr config.rootEmail]) - ] - hidden = [ InputTag [TypeAttr "hidden",NameAttr "clientPath", ValueAttr config.clientPath] - , InputTag [TypeAttr "hidden",NameAttr "staticPath", ValueAttr config.staticPath] - , InputTag [TypeAttr "hidden",NameAttr "sessionTime",ValueAttr (toString config.sessionTime)] - , InputTag [TypeAttr "hidden",NameAttr "serverPort",ValueAttr (toString config.serverPort)] - , InputTag [TypeAttr "hidden",NameAttr "serverPath", ValueAttr config.serverPath] - , InputTag [TypeAttr "hidden",NameAttr "debug", ValueAttr (if config.debug "true" "false")] - , InputTag [TypeAttr "hidden",NameAttr "smtpServer", ValueAttr config.smtpServer] - , InputTag [TypeAttr "hidden",NameAttr "generalWorkflows", ValueAttr (if config.generalWorkflows "true" "false")] - , InputTag [TypeAttr "hidden",NameAttr "runAsyncPath", ValueAttr config.runAsyncPath] - , InputTag [TypeAttr "hidden",NameAttr "curlPath", ValueAttr config.curlPath] - ] - -errclass error = if (isNothing error) "field-ok" "field-error" -errmsg Nothing = [] -errmsg (Just msg) = [EmTag [] [Html msg]] diff --git a/Server/Framework/Store.icl b/Server/Framework/Store.icl index 874c4374e1984231cea049883e1f4b9ed6886c44..a9cd93d282c3cb87a19d8c5ace89e6e8b5950258 100644 --- a/Server/Framework/Store.icl +++ b/Server/Framework/Store.icl @@ -2,9 +2,8 @@ implementation module Store import StdString, StdArray, StdChar, StdClass, StdInt, StdFile, StdList, StdTuple, StdMisc, Void import File, Directory, OSError, Maybe, Map, Text, JSON, Functor, FilePath -from Config import :: Config from IWorld import :: IWorld(..), :: ProcessId, :: Control -from SystemTypes import :: DateTime, :: User +from SystemTypes import :: DateTime, :: User, :: Config from Time import :: Timestamp(..), instance < Timestamp, instance toInt Timestamp from iTasks import serialize, deserialize, defaultStoreFormat diff --git a/Server/Framework/WebService.icl b/Server/Framework/WebService.icl index dd6d15473c46a2d49207f77cf7fb1d3444bd58cd..fe242b1f51200ae42abbbec0f158e9abb66e1493 100644 --- a/Server/Framework/WebService.icl +++ b/Server/Framework/WebService.icl @@ -160,3 +160,19 @@ where = jsonResponse (toJSON val) plainDoneResponse _ = errorResponse "Corrupt result value" + + appStartResponse appName = {newHTTPResponse & rsp_data = toString (appStartPage appName)} + + appStartPage appName = HtmlTag [] [head,body] + where + head = HeadTag [] [TitleTag [] [Text "Loading..."]: styles ++ scripts] + body = BodyTag [] [] + + styles = [LinkTag [RelAttr "stylesheet", HrefAttr file, TypeAttr "text/css"] [] \\ file <- stylefiles] + scripts = [ScriptTag [SrcAttr file, TypeAttr "text/javascript"] [] \\ file <- scriptfiles] + + stylefiles = ["/lib/ext-4.0.2a/resources/css/ext-all-gray.css" + ,"/src/css/main.css" + ,appName +++ ".css"] + scriptfiles = ["/lib/ext-4.0.2a/ext-debug.js","/src/app.js"] + \ No newline at end of file