There are two sets of instructions for an activity: online and offline.
In the authoring screen, there should be an text entry field and a way to upload files for both online and offline instructions.
The text entry field is a large entry field, which should is backed by a text column (or similar blob type column) in the database. This field should be implemented using the FCKEditor, allowing the user to enter formatted text. If your tool does not use the FCKEditor it will look different to the other tools and may confuse users.
The author should be able to attach one or more files, which would be stored in the content repository.
When the sequence is run, the teacher must be able to access the instructions from the Monitor screen's Instructions tab.
Note: There are also instructions for the sequence as a whole, but they will be covered elsewhere, and are not the responsibility of the tool writer.
ToolContentHandler and ToolDownload - A simple way to use the Content Repository
If your tool uses the content repository for storing these files and nothing else, then you can use the org.lamsfoundation.lams.contentrepository.client package classes to do most of the content repository calls for you.
ToolContentHandler allows you to store a file in the content repository. Every file that is stored needs to be marked as either an Offline or Online file. It is a very simple client, in that it only handles files (and not packages) and will not keep versions of files - to do a new version of a file, your code must delete the old file and add the new file. The unique id of the file will change.
For downloading or viewing the file, you can use ToolDownload. This is a special version of the Download servlet and it ties in with the ToolContentHandler. The ToolDownload/Download servlets are powerful enough to handle versions and packages.
If the ToolContentHandler is too restricted for your use, or you want to use the content repository to store other things, then you will need to do your own code to access the repository. You will then need to implement your own version of the Download servlet (from the same package). But don't panic - you just need to do the code to access the content repository, and you can probably pinch some of that from ToolContentHandler anyway!
To use the ToolContentHandler, you will need to
- extend the ToolContentHandler class, which implements IToolContentHandler
- define an bean of the class in your Spring environment
- define the ToolDownload servlet in your web.xml and include the name of your handler bean as an initialisation paramter.
Examples of the Spring bean and servlet definition. (Note: the servlet will also need the mapping defined.)
For more information on these classes, please read the Javadoc in the lams_contentrepository project, in the doc directory.
In general in authoring, no data is updated in the database until the user presses Save. This allows the user to "cancel" their changes by closing the authoring window without saving. This is easy for most fields as the data can be kept in the form. But it isn't as easy with the uploaded files.
Our strategy for handling the files is as follows. Please use this strategy as it will help us to maintain the code into the future. Uploading the files to a temporary area is reasonably complex, so we have compromised by storing the files in the content repository (which updates the content repository tables in the database) but we don't store the details in the tool tables until the user clicks Save.
There is a POJO that describes a file and its details, which is called Attachment object in this description. In the case of noticeboard, this is NoticeboardAttachment. We keep two collections of Attachments - attachmentList and deletedAttachmentList.
On authoring start:
When the authoring page starts, the attachmentList needs to be initialised from tool tables. If easier for your coding, add deletedAttachmentList at the same time, or add it later when it is required.
In the noticeboard, we store the lists in the session, rather than in the request. This is probably the easiest way to do it. The downside is that the attachments stay in the session until next time the authoring screen for the tool is run.
On user clicking the upload button:
- Add the file to the content repository and add a matching Attachment object to the attachmentList.
- If a file with the same name does exists in either the attachmentList then move the Attachment object for the old file to the deletedAttachmentList.
On user clicking the delete button:
- The Attachment object is moved from the attachmentList to the deletedAttachmentList. No changes are made to the content repository.
On user clicking the save button:
- Go through the attachmentList and persist all the new values in this list to the tool tables. You can detect the new values by checking the ID field of the Attachment object - if it is null then it is a new object. If it isn't null, we assume that it is already in the database. (This is based on how we do our primary keys using Hibernate.)
- Go through the deletedAttachmentList, remove any entries in the list from the content repository and from the tool tables. There should always be matching entries in the content repository (unless there is a bug) but there may or may not be matching entries in the tool tables.
Advantages of this strategy: No need to store files in a temporary area. Tool tables aren't changed until the user presses Save.
Disadvantages of this strategy: Complexity of having an second collection of attachment objects. If the user uploads a file then closes the authoring window without saving we end up with junk in the content repository (which we can live with).
(A) Don't have the deletedAttachmentList collection
Instead of maintaining a list of things to delete, we delete any entries from the tool tables (and their matching entries in the content repository) where the entry does not exist in the attachmentList. i.e. if it isn't in the attachmentList when the user presses Save, then it is removed from the database. This is simpler but it will (1) leave more junk in the content repository and (2) if the attachmentList collection should get lost from the request/session due a bug, then it will delete all the instruction attachments for that activity. The latter problem could cause a nasty surprise to a user.
(B) Allow the user to select multiple files but don't do the upload until the Save button is clicked.
(C) When the file is uploaded, it is stored in a temporary area rather than the content repository.
Then when the user presses save, the file is stored in the content repository. If we do this, we still need the delete logic, and we also have to write the code that writes the files to the temporary area, manages overlapping filenames, deletes the files as they are stored in the content repository, etc. Not a huge amount of code but its not three lines either! The main advantage of this change would be that we wouldn't end up with junk in the content repository, as the temporary file area could be cleaned up on a regular basis.