SoftChalk Devel Notes - 2008

Oct. 27, 2008
-------------
Downloaded Sakai 2.5.3 from SVN, built, and deployed it into a clean Tomcat 5.5.26. Starts up with out problems.

Attempted to create a user called "coursefeedgate". User account is present, but I cannot log into it.

Oct. 28, 2008
-------------
Looked at user tables in Sakai database. No changes to table structure. Data for coursefeedgateway seems to be ok. Deleted cfg. Create a new student (betty). That worked ok and oculd log in as her. Re-created coursefeedgateway as type "admin". Now able to log in as cfg. Added cfg to Admin Workshpace. Logged in again and can see the workspace.

Wrote off to John Leasia to see how an admin account can be created. The cfg account doesn't have visibility into all site courses, so I doubt it will serve as a good gateway.

John replied:

Create the account as normal, then go to the admin site (site with
site id of !admin - default name is Administration Workspace). You can
either use the Admin Realms tool and 'grant ability' to that account,
or go to the site and use Site Info to add that account to the site.
Any account that is member of that site (with the admin role in that
site) will be just as the admin user.

Yes - if you just want to give access to all course sites (all that are created from now on after the edit) you could add the user to the !site.template.course realm. I see you found the !admin realm too..

classtop
Oct. 29, 2008
-------------

Logged onto demo73.classtop.com via Windows Remote Desktop (Accessories -> Communication -> Remote Desktop).
Admin password is "fix2Build"

That system is running Java 1.6, which may be a problem.
Downloaded and installed maven 2.0.9.
Downloaded and installed subversion 1.5.2

Rich Christiansen indicates that MySQL is installed -uroot -pfix2Build.

Oct. 30,2008
------------

Printed out install procedure.
Checked out https://source.sakaiproject.org/svn/sakai/tags/sakai_2-5-3/

Tried to compile, memory shortage.
Downloaded Tomcat 5.5.27.
Set CATALINA_HOME
Tried to startup tomcat, but got error:

The JAVA_HOME environment variable is not defined correctly
This environment variable is needed to run this program
NB: JAVA_HOME should point to a JDK not a JRE

Downloaded JDK 1.5.0_16.

Added several environment variables: JAVA_OPTS, MAVEN_OPTS.
Added JDK and JRE 1.5.0_16 to Path.

Started Sakai to compile.
Filed once (JUnit tests).
Added settings.xml file in Admin/.M2 with test switch off.
Re-compiled and deployed successfully.

Somewhere along the lines, I brough the Blackboard server down, likely when I forced a shutdown on tomcat. Switched the Sakai tomcat to run on port 8090 and can access it from both demo73 and my home computer system. Bb was restarted by John Dupaix at ClassTop. Started ok, but can't access it normally. The url, "http://demo73.classtop.com" should go to Bb.

Downloaded MySQL 5.0.67

Oct.31, 2008
------------

Installed and configured MySQL 5.0.67.
Root password is fix2Build.

Data base initialized with the following commands:
create database sakai default character set utf8;
grant all privileges on sakai.* to 'sakai'@'localhost' identified by 'ironchef';
flush privileges;
quit

Verified that Sakai database was created.

Had a look at the maven settings.xml file. The maven.tomcat.home variable had been set to "c:/dev/apache-tomcat-5.5.267", which is how that directory got created. Sakai wasn't deployed to the correct tomcat previously. Changed settings and completely rebuilt and redeployed Sakai.

Failed to start the first few times. Needed to get configuration parameters right for database (added user and pw for database, autoddl, etc.)

Sakai now comes up fully. Able to login as admin.
Mail from John Dupaix indicated that he needed the system today, so further work is suspended.

Nov. 1, 2008
------------

Sakai didn't start. Re-started MySQL server. – Make a note of this in documentation.
Tomcat startup still crashing. Installed Cygwin.
Unable to install Cygwin - keeps crashing during install.

Shifted to work on local instance of Sakai.
Created an RSF stuf application for CourseFeed registration.
The idea is to have a very simple tool that can be included in a user's MyWorkspace either on the homepage or as a separate tool. The tool either shows a message saying that they are already registered for CourseFeed or shows a form to "Register Now". Form takes the user's FaceBook id.

For a tool to show in in MyWorkspace, it must include "<category name="myworkspace" />" in the tool registration file.

Nov. 6, 2008
------------
Re-created coursefeed tool in sakai_2-5-3 on base computer using RSFExample/rsf-ex-0/. Builds and deploys without problems.

I think a database table is going to be needed for the registration application and gateway. We need to be able to map a signature key to a user name in order to have access to the user via the gateway. While we could store the access key as a user property, there is no easy way to search for a user given the property value.

Two strings are needed:

username 256
accesskey 256

I plan to use the IdManager to generate a unique key string. Naturally, it's only unique to a particular Sakai installation, but that's good enough for this application. I'm not quite sure how the autoddl mechanism works in Sakai. I 'think' it's just a matter of creating the DDL files, putting them in the right place of the directory structure, and including them in the JAR/WAR. What causes them to be used, though?

Nov. 7, 2008
------------
There will need to be a CourseFeed service to manage the database table(s). I'd be tempted to just embed this into the cf-tool, but the service will be needed by the gateway service, and thus needs to be shared across components. It is also the recommended pratice (to shift data management into it's own application service).

Created a CourseFeed service by cloning sousa service. The API consists of the following methods:

public Boolean hasUserKey();
public String getUserKey ();
public String getUserFromKey(String key);

These are implemented as hardwired string returns.

Ran into problems with the object type of service returned from ComponentManager. I should be able to cast CourseFeedServiceImpl to CourseFeedService, but I get a class cast error.
Solution: the API reference in the tool POM needed to have a scope of "provided". Corrected and access to service is working properly.

Tested service for registrations status both ways (registered and unregistered). All stubs are working correctly.
Now I need to start working on the databases.

Nov. 10, 2008
-------------

Created an access key table manually using the following SQL statements:

create table classtop_access (username varchar(255) not null, accesskey varchar(255) not null);
insert into classtop_access values ("test", "test-access-key");

A single test record is included to check out table creation. Likely, this table could be made better via keys or a primary key. I also need to figure out how such tables are setup using auto DDL. Make username unique.

Came across this for cm-session:
insert into cm_academic_session_t values(26, 1, 'admin', SYSDATE,'admin', SYSDATE, 'WINTER 2009', 'WINTER 2009', 'W09', '07-JAN-2009','21-APR-2009');

Some notes from using the SqlService:

1. Create an interface with methods to return SQL statements.
2. Create a default implementation.
3. Create variants for databases.

It is somewhat facinating to note that there is almost no documentation on the SQL Service provided by Sakai, in part because this is legacy code that extends clear back to CHEF.

Query to check for registered user:

String checkQuery = "select * from classtop_access where (username='"eid"');"

Query strings might be eternalizable using Tomcat resource loader.
Implemented CourseFeedService.hasUserKey() against a MySQL database. Seems to work correctly.

Implemented CourseFeedService.registerUser().

It might be a good idea to include an unregister link.

Chuck says that queries are not exernalized, only the auto-ddl files. The rest are put into a pattern of classes that implement an interace that provides query strings, like the rSmart/John Ellis example.

Completed remaining methods in CourseFeedService. Tested, all work.
Backed up to zip file - coursefeed-111008.zip.

===============

Time to start on the web services, now that I have I have access key codes in place.
Well, surprise. The CourseFeed services are implemented as REST, not web service. I had a look at the EntityManager, which is implemented as a Servlet. They handle all of the REST commands, but CourseFeed only needs GET. That simplifies things quite a bit.

Create the start of the gateway by copying the EntityBroker applet.

Having problems accessing the servlet. The following comes closest:

http://localhost:8080/cfgateway/

But that says that classtop.coursefeed.gateway is not available.
The package name was wrong.

Created a basic REST application end point by cloning EntityBroker. Compiles, deploys, and works with hello world text.

Nov. 11, 2008
-------------

The following REST actions will need to be coded:

IntegrateFacebook - this is how the link from facebook to Sakai actually happens.
GetUserInfo - various user data.
GetCourseList (all = false) - just a list of courses.
GetCourseList (all = true) - list of courses and all content.
GetCourseRoster - list of persons with email.
SendCourseEmail - send an email message.

Installed tools onto demo71 including subversion and maven. Created environment variables. Installed sakai, compile and deployed it. Runs locally, but not from internet. Port 8080 needs to be opened up.

Resumed work on REST requests, specifically the IntegrateFacebook request.
Created a debug variable that can be detected in the REST request (debug=t) If present, it parses out the query parameters and shows them. Also shows any cookies.

The IntegrateFacebook request is the one that causes a record to be created in the classtop_access database. The table needs to be modified to consist of a sakai user Id and a facebook id. Once established, requests are validated against the facebook id in this table and information is gathered in the cfgateway user role.

Now Ian has indicated that a request needs to be made of the CF Registration tool from CourseFeed that contains the facebook id, handshake and two URLs (for for success, one for failure). I'll need to carefully craft an RSF application node that has a ViewParameters capable of capturing the incoming parameters from CourseFeed. that shouldn't be too hard.

The step 4 URL is
http://demo71.classtop.com:8080/cfgateway
?action=IntegrateFacebook
&fbId=745478915
&hs=SQX%5b%00%0b%17RY
&success=http%3a%2f%2fapps.facebook.com%2fcoursefeed%2f%3fdisplay%3dbbconfirmed%26lmsServerId%3d123
&failed=http%3a%2f%2fapps.facebook.com%2fcoursefeed%2f%3fdisplay%3dbbconfirmed%26lmsServerId%3d123

which also gives you the URL to redirect to in step 5. One url is for success, the other is for failed.

Note that this URL is called from the user's browser while already authenticated to Sakai.

Nov. 12, 2008
-------------

Created RegisterProducer.java.
Connecting directly to this page will be difficult based on the URL:

http://localhost:8080/portal/tool/f956216a-02e0-4add-975c-a27d6efc1206/register

Furthermore, this URL is actually that of an iFrame. One solution is to shift it back to the REST endpoint and return and HTML page (instead of the normal XML page). This would present a pass/fail URL to the user in the original browser window that they attempted the registration from.

Maybe this isn't so bad. The tricky bit is to provide the CourseFeed application the tool id, which I can do with another REST call. The above URL will appear full screen, but the pass/fail links will return it to CourseFeed. This is a bit strange, since the user comes into the Sakai environment from CourseFeed and then returns to it.

Created RegisterProducer and RegisterParameters classes. RegisterParameters has parameters for all five fields defined by CourseFeed for the IntegrateFacebook action.

Changed SecondProducer to be DeleteProducer. This provides a link to unregister a user. This will be removed in the production version of the tool, but does provide a handy way to delete the current user from the classtop_access table.

The following changes were made to the CourseFeedService:
1. API and impls changed to reflect access using facebook id.
2. Added an unregister function.
3. Changed database table definition.
4. Recoded implementation to refelct database changes

Created an access key table manually using the following SQL statements:

create table classtop_access (username varchar(255) unique not null, facebook varchar(255) unique not null);
insert into classtop_access values ("test", "facebook-id");

Note that this adds uniqueness to both table fields.
After a bit of debugging, the unregister link now works.

I believe that full support for CourseFeed registration is in the cf-tool at this point. Some additional work is needed in cfgateway to provide the tool id and it will be ready for CourseFeed integration.

Created a REST request to get the Sakai CourseFeed registration application tool id. Unfortunately, ActiveTool.getId() returns the well-know id of "coursefeed", which is how it is registered as a tool. What's REALLY needed is the ToolPlacement id. For that, I need a site id. Since the user is logged in a the point of this request, I might be able to use the JSESSIONID cookie to establish the user's session. From that, get a user id, and then the id of their personal workspace.

Attempted to get the session directly from the JSESSIONID using the following:

Session s = SessionManager.getSession(sessionCookie.getValue());

but this fails, perhaps because the host id is embedded in it. Splitting the host name out of the JSESSIONID produces a valid session id. Able to get the user id from it.

The tool id in the Sites tool for Betty's workspace is:

f956216a-02e0-4add-975c-a27d6efc1206

This agrees with the URL above.
Attempt to site.getTool("coursefeed") failed. Use the wrong method. Should have used site.getToolForCommonId("coursefeed");
This returns the proper placement id needed to construct the CourseFeed URL to access the Sakai CourseFeed Registration tool.
Some clean up of the request code is needed and the REST tool id request will be done.
Code cleaned up, tested and returns tool placement id correctly.

Backed up into coursefeed-111208.zip.
Saved to server at http://www.nolaria.com/download/coursefeed-111208.zip

Copied backup to demo71, built and deployed it.

Created an account for cfgateway with a password of "classtop".
Added cfgateway to Admin site giving it admin priviledges.
Added cfgateway to the site.template.course as instructor role.
Created an account for student1 with a password of "none".

Nov. 16, 2008 - Newport Beach
-------------

Downloaded maven 2.0.9 (now needed by Sakai)
Downloaded and built Sakai 2.5.3
Some problems with existing SAKAI_SESSION and SAKAI_LOCKS tables. Dropped the tables and let them get rebuilt by AutoDDL. Seems to startup fine.

Nov. 18, 2008 - Newport Beach
-------------
Installed, compiled, and deployed ClassTop code into development environment.
Added code to CourseFeedServiceImpl.init() to create the classtop_access database table if it doesn't exist. This should be done by AutoDDL at a later date.

Table is correctly created at Sakai start-up time.

Problems with administrative setup. Tried to add gateway role to accounting course, but screwed up the permissions. Registered cfgateway as instructor instead.
Created an accounte for 'betty', but the course template didn't seem to add the tools expected. That doesn't bode well. I am have screwed up the template somehow.
No, I just forgot to include the CourseFeed tool in the !user template. The course template seems to be ok, but the academic sessions are screwed up and won't allow me to create a new session (which sucks).

More work is going to be needed to get course and user test data setup correctly. Meanwhile ...

Tested access to the coursefeed REST application via this URL:

http://localhost:8080/cfgateway?debug=t

Logged in as betty and presented a GetToolId action using this URL:

http://localhost:8080/cfgateway?action=GetToolId

the resulting tool id is:

ad7464ba-c863-4ceb-8120-37a73feee502

Started work on the GetUserInfo action via this URL:

http://localhost:8080/cfgateway?action=GetUserInfo&fbId=745478915

currently, this results in the following error:

HTTP Status 400 - Unknown REST request.

Added a new method in the CourseFeedApplet to detect an action of GetUserInfo. Initial results are stubbed to show facebook id. Runs with the following result:

<LmsUser>
<FacebookId>745478915</FacebookId>
</LmsUser>

Now we can explore getting additional information about the user.
This includes:

<LmsUser>
<FbId>745478915</FbId>
<Id>_22_1</Id>
<UserName>istiles</UserName>
<LastName>Stiles</LastName>
<MiddleName />
<FirstName>Ian</FirstName>
<Email>istiles@classtop.com</Email>

<HomeFax />
<HomePhone1 />
<HomePhone2 />
<MobilePhone />
<BusinessFax />
<BusinessPhone1 />
<BusinessPhone2 />

<IsAvailable>true</IsAvailable>
<LmsUrl> http://demo73.classtop.com/webapps/blackboard/execute/editUser?context=self_modify
</LmsUrl>
<City />
<Company />
<Country />
<Department />
<JobTitle />
<State />
<Street1 />
<Street2 />
<Title />
<WebPage />
<ZipCode />
<BirthDate Day="4" Hour24="0" Minute="0" Month="3" Second="0" Year="1988" />
<LastLoginDate Day="31" Hour24="20" Minute="23" Month="10" Second="32" Year="2008" />
<Gender>Male</Gender>
<IsInfoPublic>false</IsInfoPublic>
<wantTextMsg />
<wantAnnouncements />
<wantContentUpdates />
<wantGrades />
<textMsgEmailAddress />
<textMsgProvider />
<textMsgProviderOther />
<textMsgCellNumber />
<textCountry />
<LmsService MajorVersion="1" MinorVersion="40">
<ServerDateTime Day="1" Hour24="2" Minute="35" Month="11" Second="18" Year="2008" />
<LmsType>Blackboard</LmsType>
<LmsVersion>8.0.307.10</LmsVersion>
<AllowClassmateEmails>false</AllowClassmateEmails>
</LmsService>
</LmsUser>

The Sakai Profile tool allows the following personal information to be added:

First Name
Last Name
Nickname
Position
Department
School
Room
Personal Information
Hide only my Personal Information
Picture
None
Use University Id Picture
Use Picture URL :
Email
Home Page
Work Phone
Home Phone
Mobile Phone
Other (description)

The sakai_person_t table has the following fields:

ID
PERSON_TYPE
VERSION
UUID
LAST_MODIFIED_BY
LAST_MODIFIED_DATE
CREATED_BY
CREATED_DATE
AGENT_UUID
TYPE_UUID
COMMON_NAME
DESCRIPTION
SEE_ALSO
STREET
SURNAME
TELEPHONE_NUMBER
FAX_NUMBER
LOCALITY_NAME
OU
PHYSICAL_DELIVERY_OFFICE_NAME
POSTAL_ADDRESS
POSTAL_CODE
POST_OFFICE_BOX
STATE_PROVINCE_NAME
STREET_ADDRESS
TITLE
BUSINESS_CATEGORY
CAR_LICENSE
DEPARTMENT_NUMBER
DISPLAY_NAME
EMPLOYEE_NUMBER
EMPLOYEE_TYPE
GIVEN_NAME
HOME_PHONE
HOME_POSTAL_ADDRESS
INITIALS
JPEG_PHOTO
LABELED_URI
MAIL
MANAGER
MOBILE
ORGANIZATION
PAGER
PREFERRED_LANGUAGE
ROOM_NUMBER
SECRETARY
UID_C
USER_CERTIFICATE
USER_PKCS12
USER_SMIME_CERTIFICATE
X500_UNIQUE_ID
AFFILIATION
ENTITLEMENT
NICKNAME
ORG_DN
ORG_UNIT_DN
PRIMARY_AFFILIATION
PRIMARY_ORG_UNIT_DN
PRINCIPAL_NAME
CAMPUS
HIDE_PRIVATE_INFO
HIDE_PUBLIC_I
NFO
NOTES
PICTURE_URL
SYSTEM_PICTURE_PREFERRED
ferpaEnabled
dateOfBirth
locked

The first step is to see what of this information is available via the User Entity properties. If all, that would be convenient.

There is a Sakai Profile service that provides access to additional information on users.
There is no cover for SakaiEduPerson service.

I think the initialization logic in CourseFeedService is causing problems. If the table exists, an exception is thrown that is not caught. I suspect this causes the CFS to be unavailable to applications. Commenting this out. It should be handled by AutoDDL anyways.

Added a debug mode to GetUserInfo request. Currently shows:

Response to GetUserInfo Request
FaceBook Id: 745478915
Sakai User Id: betty

Added code to get the user object and extracted information from it:

FaceBook Id: 745478915
Sakai User Id: betty
Information from User Object:
Email: betty@none.edu
FirstName: Betty
Last Name: O'Bryan
Type: null

Next step is to get at the entity properties, then profile information.

Nov. 19, 2008 - Newport Beach
-------------

Added code to extract data from user profile.

Nov. 20, 2008 - In-flight to Pittsburg
-------------

Started work on GetCourseList.

While adding error detection around user ids, I note that the IntegrateFacebook action is not fully implemented. It is hardwired to the betty id.

Getting the list of sites works, but it gets all sites. I need to filter out only course sites, ideally accessible by the indicated user. A method has been set up to append site information, but currently only generates the site id.

Filtered by "course" type yields:

<CourseList>
<Courses>
<Course>
<CourseId>89f527e0-3edb-4b80-80c8-e8a3d354fc03</CourseId>
</Course>
</Courses>
</CourseList>

This is the single accounting class currently created in my test Sakai instance.
The list of course sites needs to be further filtered by user membership. This can be done in the addCourseInfo() method by checking the Site passed for user membership.

Course data includes:

<Title>Herbology 101</Title>
<CourseDisplayId>herbology101</CourseDisplayId>
<AllowGuests>true</AllowGuests>
<AllowObservers>false</AllowObservers>
<CreatedDate Day="31" Hour24="21" Minute="3" Month="10" Second="30" Year="2008"/>
<ModifiedDate Day="31" Hour24="21" Minute="3" Month="10" Second="30" Year="2008"/>
<Id>_16_1</Id>
<Fee>0.0</Fee>
<InstitutionName></InstitutionName>
<IsAvailable>true</IsAvailable>
<IsLockedout>false</IsLockedout>
<NavColorBackground>0x336666</NavColorBackground>
<NavColorForeground>0xFFFFFF</NavColorForeground>
<LmsUrl>http://demo73.classtop.com/webapps/portal/frameset.jsp?tab=courses&amp;url=/bin/common/course.pl?course_id=_16_1</LmsUrl>
<CanContainFolders>true</CanContainFolders>
<BodyFormat>PlainText</BodyFormat>
<Body>The study and application of herbs.</Body>
<QuotaMax>-1</QuotaMax>
<QuotaRemaining>-1</QuotaRemaining>
<QuotaCurrent>1024</QuotaCurrent>

Most of the information required by GetCourseList is now present:

<CourseList>
<Courses>
<Course>
<InstitutionName/>
<CanContainFolders>true</CanContainFolders>
<LmsUrl/>
<Body>Warren accounting course</Body>
<IsAvailable>true</IsAvailable>
<QuotaRemaining/>
<IsLockedout/>
<NavColorForeground>FFFFFF</NavColorForeground>
<QuotaCurrent/>
<AllowObservers>true</AllowObservers>
<NavColorBackground>000000</NavColorBackground>
<BodyFormat>PlainText</BodyFormat>
<AllowGuests>true</AllowGuests>
<CreatedDate/>
<QuotaMax/>
<Title>ACC_11_1_Fall2007</Title>
<ModifiedDate/>
<Id>89f527e0-3edb-4b80-80c8-e8a3d354fc03</Id>
<CourseDisplayId>ACC_11_1_Fall2007</CourseDisplayId>
<Fee/>
<CreatedDate Day="10" Hour24="14" Minute="36" Month="9" Second="41" Year="2007"/>
<ModifiedDate Day="10" Hour24="13" Minute="45" Month="6" Second="12" Year="2008"/>
</Course>
</Courses>
</CourseList>

Todo:

  • Filter course sites by user access.
  • Convert GetUserInfo to method used in course info.
  • Finish installFacebook request
  • Add the "Full" flag to course info to show content, announcements, assignnments, etc.

Nov. 24, 2008 - Willseyville
-------------

Went back to fully implement the IntegrateFacebook request. All is fine, except that the CourseFeedService.register() method only takes a facebook id and assumes the current user. This is based on registering a student from the CourseFeed application. It think it's better to just pass in the user to be regsitered. Changed API and implementation to take a Sakai user id (EID) in addition to the facebook id.

The above is a mistake. The IntegrateFacebook request happens in the CourseFeed tool and should use the current user. However, it could just as easily be done using the CourseFeed servlet, instead and wouldn't require getting the tool id of the facebook tool. It's partially implemented in the servlet at this point. It just needs to redirect the result to either the fail or success urls.

Actually, the call to CourseFeedService.registerUser() uses the real userId, rather than the eid. I'd need to extract the EID from the session to do it this way. Not worth it, since I've already told ClassTop to access the CourseFeed tool using the GetToolId Rest request.

Todo:

  • Filter course sites by user access.
  • Convert GetUserInfo to method used in course info.
  • Add the "Full" flag to course info to show content, announcements, assignnments, etc.

Nov. 25, 2008 - Willseyville
-------------

Start work on coverting GetUserInfo to key/value map technique.
Removed the old method and also deleted the GetUserInfoText() version. It is no longer needed.
Data now generated:

<LmsUser>
<FacebookId>745478915</FacebookId>
<BixPhone2/>
<HomeFax/>
<Email>betty@none.edu</Email>
<HomePhone2/>
<MobilePhone/>
<FirstName>Betty</FirstName>
<BizFax/>
<BizPhone1>617-555-1212</BizPhone1>
<UserName>betty</UserName>
<Id>4d86909b-0a2f-4a93-abfe-869400f9cd8a</Id>
<HomePhone1>508-444-1212</HomePhone1>
<LastName>O'Bryan</LastName>
<CreatedDate Day="18" Hour24="20" Minute="13" Month="11" Second="6" Year="2008"/>
<ModifiedDate Day="18" Hour24="20" Minute="13" Month="11" Second="6" Year="2008"/>
</LmsUser>

Stubbed out additional data desired by CourseFeed.
Working, now generates:

<LmsUser>
<FacebookId>745478915</FacebookId>
<HomeFax/>
<textMsgEmailAddress/>
<Street2/>
<textCountry/>
<IsInfoPublic>false</IsInfoPublic>
<textMsgCellNumber/>
<ZipCode/>
<City/>
<IsAvailable/>
<wantGrades>true</wantGrades>
<BirthDate/>
<FirstName>Betty</FirstName>
<BizFax/>
<Street1/>
<LastLoginDate/>
<UserName>betty</UserName>
<Department/>
<JobTitle>Junior</JobTitle>
<BixPhone2/>
<Gender/>
<MobilePhone/>
<State/>
<textMsgProviderOther/>
<wantTextMsg>true</wantTextMsg>
<LmsUrl/>
<wantContentUpdates/>
<BizPhone1>617-555-1212</BizPhone1>
<textMsgProvider/>
<Email>betty@none.edu</Email>
<HomePhone2/>
<WebPage>http://www.classtop.com</WebPage>
<Country/>
<Company/>
<Title/>
<Id>4d86909b-0a2f-4a93-abfe-869400f9cd8a</Id>
<wantAnnouncements>true</wantAnnouncements>
<HomePhone1>508-444-1212</HomePhone1>
<LastName>O'Bryan</LastName>
<CreatedDate Day="18" Hour24="20" Minute="13" Month="11" Second="6" Year="2008"/>
<ModifiedDate Day="18" Hour24="20" Minute="13" Month="11" Second="6" Year="2008"/>
</LmsUser>

Some additional information may be available in SakaiPerson, if supported by the Sakai instance.

Todo:

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
  • Filter course sites by user access.
  • Check SakaiPerson for additional personal information.
  • Ask about the "want" entries in user data. Should these be maintained by the CourseFeed application?
  • Add the "Full" flag to course info to show content, announcements, assignnments, etc.
  • Implement the GetCourseRoster request.
  • Implement the SendCourseEmail request.

Nov. 26, 2008 - Bedford
-------------

Sent off a note to Jayson and Ian concerning data wants.
Started work on GetCourseRoster request.
Completed coding of GetCourseRoster request.

On base computer, using the URL:

http://localhost:8080/cfgateway?action=GetUserInfo&fbId=745478915&CourseId=310a0268-9c2b-4b5b-950a-689937088409

This is the Algebra_1_1_fall2008 course with two users: mark and betty.

Ran the URL. Only got the admin user:

<LmsUsers>
<LmsUser>
<Email/>
<FirstName>Sakai</FirstName>
<CourseRole>Instructor</CourseRole>
<UserName>admin</UserName>
<Id>admin</Id>
<LastName>Administrator</LastName>
</LmsUser>
</LmsUsers>

Note that admin has an instructor role. Also got the following error messages in the console:

CourseFeedServlet - GetRosterInfo - Unknown user: 2b16cfd9-79cd-45c7-b831-d50e6d03582b
CourseFeedServlet - GetRosterInfo - Unknown user: 820f96cd-6c9b-4884-83d7-a8a2b15294a7
CourseFeedServlet - GetRosterInfo - Unknown user: 87eb2091-bc5b-4a17-bc48-95ff3d47b2c3

This looks like a mismatch in id's expected.
Confirmed. Code was using getUserByEdit() instead of getUser().

XML produced now reads:

<LmsUsers>
<LmsUser>
<Email>cassidy@none.edu</Email>
<FirstName>Brigid</FirstName>
<CourseRole>Student</CourseRole>
<UserName>bcassidy</UserName>
<Id>2b16cfd9-79cd-45c7-b831-d50e6d03582b</Id>
<LastName>Cassidy</LastName>
</LmsUser>
<LmsUser>
<Email>betty@none.edu</Email>
<FirstName>Betty</FirstName>
<CourseRole>Student</CourseRole>
<UserName>betty</UserName>
<Id>820f96cd-6c9b-4884-83d7-a8a2b15294a7</Id>
<LastName>OBryan</LastName>
</LmsUser>
<LmsUser>
<Email>mark@none.edu</Email>
<FirstName>Mark</FirstName>
<CourseRole>Instructor</CourseRole>
<UserName>mark</UserName>
<Id>87eb2091-bc5b-4a17-bc48-95ff3d47b2c3</Id>
<LastName>Norton</LastName>
</LmsUser>
<LmsUser>
<Email/>
<FirstName>Sakai</FirstName>
<CourseRole>Instructor</CourseRole>
<UserName>admin</UserName>
<Id>admin</Id>
<LastName>Administrator</LastName>
</LmsUser>
</LmsUsers>

This is exactly right.

Trying to set the "mark" user up as a CourseFeed user. The URL used was:

http://localhost:8080/portal/tool/e8f6a3e9-6bea-400a-b804-a322fb17bc6e/register?action=IntegrateFacebook&fbId=99999999&success=pass.htm&fail=fail.htm

The GetUserInfo request URL is:

http://localhost:8080/cfgateway?action=GetUserInfo&fbId=99999999

and results in

<LmsUser>
<FacebookId>99999999</FacebookId>
<Street2/>
<textMsgEmailAddress/>
<textCountry/>
<ZipCode/>
<textMsgCellNumber/>
<City/>
<IsAvailable/>
<wantGrades>true</wantGrades>
<BirthDate/>
<FirstName>Mark</FirstName>
<Street1/>
<LastLoginDate/>
<Department/>
<UserName>mark</UserName>
<Gender/>
<State/>
<textMsgProviderOther/>
<LmsUrl/>
<wantTextMsg>true</wantTextMsg>
<wantContentUpdates/>
<textMsgProvider/>
<Email>mark@none.edu</Email>
<Country/>
<Company/>
<Title/>
<Id>87eb2091-bc5b-4a17-bc48-95ff3d47b2c3</Id>
<wantAnnouncements>true</wantAnnouncements>
<LastName>Norton</LastName>
<CreatedDate Day="15" Hour24="10" Minute="23" Month="7" Second="0" Year="2008"/>
<ModifiedDate Day="26" Hour24="15" Minute="32" Month="11" Second="29" Year="2008"/>
</LmsUser>

The simple GetCourseList request is:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999

This returns:

<CourseList>
<Courses>
<Course>
<InstitutionName/>
<CanContainFolders>true</CanContainFolders>
<LmsUrl/>
<Body>A test site for course management, gradebook, etc.</Body>
<IsAvailable>true</IsAvailable>
<QuotaRemaining/>
<IsLockedout/>
<NavColorForeground>FFFFFF</NavColorForeground>
<QuotaCurrent/>
<AllowObservers>true</AllowObservers>
<NavColorBackground>000000</NavColorBackground>
<BodyFormat>PlainText</BodyFormat>
<AllowGuests>true</AllowGuests>
<CreatedDate/>
<QuotaMax/>
<Title>ACCT_100_1_fall2008</Title>
<ModifiedDate/>
<Id>bb1438e3-0508-4ac7-a3fb-7872fc016e38</Id>
<CourseDisplayId>ACCT_100_1_fall2008</CourseDisplayId>
<Fee/>
<CreatedDate Day="14" Hour24="11" Minute="24" Month="7" Second="2" Year="2008"/>
<ModifiedDate Day="12" Hour24="15" Minute="58" Month="8" Second="58" Year="2008"/>
</Course>
<Course>
<InstitutionName/>
<CanContainFolders>true</CanContainFolders>
<LmsUrl/>
<Body>A test site for algegra.</Body>
<IsAvailable>true</IsAvailable>
<QuotaRemaining/>
<IsLockedout/>
<NavColorForeground>FFFFFF</NavColorForeground>
<QuotaCurrent/>
<AllowObservers>true</AllowObservers>
<NavColorBackground>000000</NavColorBackground>
<BodyFormat>PlainText</BodyFormat>
<AllowGuests>true</AllowGuests>
<CreatedDate/>
<QuotaMax/>
<Title>Algebra_1_1_fall2008</Title>
<ModifiedDate/>
<Id>310a0268-9c2b-4b5b-950a-689937088409</Id>
<CourseDisplayId>Algebra_1_1_fall2008</CourseDisplayId>
<Fee/>
<CreatedDate Day="28" Hour24="12" Minute="14" Month="10" Second="29" Year="2008"/>
<ModifiedDate Day="28" Hour24="12" Minute="14" Month="10" Second="30" Year="2008"/>
</Course>
</Courses>
</CourseList>

This request includes a role set. The example given is "roles=*", which presumably is all roles. I would assume that roles can be comma separated, but it will be important that this list contain only legitimate Sakai roles.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList
  • Filter course sites by user membership.
  • filter course sites by role given.
  • Add the "Full" flag to course info to show content, announcements, assignnments, etc.
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.

Nov. 28, 2008
-------------

A "History 101" course had been previously created as a project site. Changed site type to course using Site tool. This is a hack way around the problem with creating sessions. With this in place, I should be able to filter GetCouresList by user (and later by role).

Ran GetCourseList URL (above) using the "mark" user. The following course titles were found:

<Title>ACCT_100_1_fall2008</Title>
<Title>Algebra_1_1_fall2008</Title>
<Title>History 101</Title>

Mark is a member of the first two, but not the last. It should not appear in the course list. Only admin is a member.
Adding the following code:

// If user is not a member of this site, skip over it.
if (site.getMember(user.getId()) == null)
continue;

Caused the History course to be excluded when mark made the request.

Mark is a student in one course and an instructor in another. To test role based filtering of GetCourseList, the following cases must be considered:

no param 2 courses (all)
no roles 2 courses (all)

  • 2 courses (all)
    Student 1 course
    Instructor 1 course
    Student,Instructor 2 courses
    TA 0 courses

The following URL is used to test role-based filtering:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*

List of roles created based on use cases above.
Note that course roles use initial caps.
Student, Instructor, and Student/Instructor cases all work. Wild card does not.
Added code to check for wild card.

Test results:

no param
<Title>ACCT_100_1_fall2008</Title>
<Title>Algebra_1_1_fall2008</Title>
no roles
<Title>ACCT_100_1_fall2008</Title>
<Title>Algebra_1_1_fall2008</Title>
*
<Title>ACCT_100_1_fall2008</Title>
<Title>Algebra_1_1_fall2008</Title>
Student
<Title>ACCT_100_1_fall2008</Title>
Instructor
<Title>Algebra_1_1_fall2008</Title>
Student,Instructor
<Title>ACCT_100_1_fall2008</Title>
<Title>Algebra_1_1_fall2008</Title>
TA
no courses

All uses cases pass.

The GetCourseList may specify a list of course ids. The following use cases are considered:

no param All valid courses.
no ids All valid courses.

  • All valid courses.
    one invalid id No courses.
    one id One course if user is a member, otherwise none.
    many comma delim'd ids Each course if user is a member.

The following URLs are used to test site id filtering:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&ids=*
http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&ids=bb1438e3-0508-4ac7-a3fb-7872fc016e38
http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&ids=no-such-course
http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&ids=bb1438e3-0508-4ac7-a3fb-7872fc016e38,da0f14db-e0a1-44fe-9608-0d8f7c4af0bd

Re-wrote the section on generating the list of course sites to use a list of site ids if provided.
All uses cases tested correctly.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList
  • Add the "Full" flag to course info to show content, announcements, assignnments, etc.
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.

Nov. 29, 2008
-------------

Ian confirmed that roles and ids lists are comma separated where appropriate. Wild cards always apply only to sites the user has access to.
Will start work on the "wantAll" flag today.

The "wantAll" flag indicates that content entries associated with a course site should be included.
Extracted a course entry with content data included into the file content-data.xml.

The XML structure describing folders and files is a bit complex. The following rules apply:

  • A set of folder entries is contained in a <ContentFolders> tag.
  • If a folder contains sub-folders, it must be contained in a <ContentFolders> tag.
  • Each folder is described by a <ContentFolder> tag.

It looks like visibility or access has something to do with what is included, as would be expected.
All <ContentFolder> entries have the following tags included in them:

<Title>External Links</Title>
<ContentHandler>Folder</ContentHandler>
<CourseId>_16_1</CourseId>
<Id>_108_1</Id>
<ParentId>0</ParentId>
<PositionalOrder>7</PositionalOrder>

Folders that contains sub-folders have the following tags in addition to those above:

<AllowGuests>true</AllowGuests>
<AllowObservers>true</AllowObservers>
<IsAvailable>true</IsAvailable>
<ExternalLink></ExternalLink>
<ExternalLinkInNewWindow>false</ExternalLinkInNewWindow>
<TitleColor>000000</TitleColor>
<CreatedDate Day="31" Hour24="21" Minute="3" Month="10" Second="31" Year="2008"/>
<ModifiedDate Day="31" Hour24="21" Minute="18" Month="10" Second="20" Year="2008"/>
<LmsUrl>http://demo73.classtop.com/webapps/blackboard/content/listContentEditable.jsp?content_id=_108_1&amp;course_id=_16_1&amp;mode=quick</LmsUrl>
<WhatTheCrap>Folder</WhatTheCrap>
<CanContainFolders>true</CanContainFolders>
<IsTracked>false</IsTracked>

Notes that AllowGuests, AllowObservers, and IsAvailable are all true. This implies open access.

  • Folders that contain files have a <ContentFiles> tag.
  • Each file is described by a <ContentFile> tag.

Files have the following tags:

<StorageSystem>DOS</StorageSystem>
<FileSystemEntryType>File</FileSystemEntryType>
<Title>STAPLES.GIF</Title>
<LinkName>My Name of Link to Image</LinkName>
<ModifiedDate Day="31" Hour24="21" Minute="12" Month="10" Second="45" Year="2008"/>
<Size>57707</Size>
<CourseId>_16_1</CourseId>
<Id>_110_1~_26_1</Id>
<ParentId>_110_1</ParentId>
<Url>http://demo73.classtop.com/courses/1/herbology101/content/_110_1/STAPLES.GIF</Url>
<IsAvailable>true</IsAvailable>
<DisplayMethod>Link</DisplayMethod>

I'm not sure that traversing the content heirarchy using the Sakai Content Hosting Service will logically group folders and files together. Probably the first pass at generating XML representations should be done using a simple <folder> contains <folder> contains <file> structure. Then I can fix it up to match the syntax provided by ClassTop.

First attempt to is just display the root node.
URL to test this is:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&wantAll=true&ids=bb1438e3-0508-4ac7-a3fb-7872fc016e38

Tried this with the result of:

CourseFeedServlet: user mark has a role of Student in site ACCT_100_1_fall2008
CourseFeedServlet: error while getting the root folder of a course site.
org.sakaiproject.exception.PermissionException user=null lock=content.read resource=/content/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/

It looks like the cfgateway user will need to be logged into Sakai in order to access content.
Logged in as coursefeedgateway. If wantAll is false, course info is displayed. If true, output is empty.
Node names were reversed in creating the <ContentFolder> node. Fixed.
Working better. The coursefeedgateway user must be logged in. Root folder accessed, but title is not displayed. Trying again using PROP_DISPLAY_NAME property.
Also added a node for the id. Along with the course information, the following content information is now included:

<ContentFolder>
<Title>ACCT_100_1_fall2008</Title>
<Id>/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/</Id>
</ContentFolder>

Correct so far. Now we attempt to traverse the collection hierarchy.
Content structure generated as follows:

<ContentFolder>
<Title>ACCT_100_1_fall2008</Title>
<Id>/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/</Id>

<ContentFolder>
<Title>Aikido</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Aikido/
</Id>
<ContentFile>
<Title>Aikido Slide Show</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Aikido/Aikido Slide Show
</Id>
</ContentFile>
<ContentFile>
<Title>Aikido Techniques</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Aikido/Aikido Techniques
</Id>
</ContentFile>
<ContentFile>
<Title>Aikido Welcome Title</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Aikido/Aikido Welcome Title
</Id>
</ContentFile>

... several more files

</ContentFolder>

<ContentFolder>
<Title>Graphics</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Graphics/
</Id>
<ContentFile>
<Title>Data Table Page</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Graphics/Data Table Page
</Id>
</ContentFile>
<ContentFile>
<Title>Final Number Page</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Graphics/Final Number Page
</Id>
</ContentFile>

... several more files
</ContentFolder>
-
<ContentFolder>
<Title>Moscow</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/
</Id>
<ContentFile>
<Title>Ballroom</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/Ballroom.jpeg
</Id>
</ContentFile>
<ContentFile>
<Title>Russia Pictures</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/Russia Pictures
</Id>
</ContentFile>
<ContentFile>
<Title>St. Basils Page</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/St. Basils Page
</Id>
</ContentFile>
<ContentFile>
<Title>St. Basils Text</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/St. Basils Text
</Id>
</ContentFile>
<ContentFile>
<Title>St. Basils Title</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/St. Basils Title
</Id>
</ContentFile>
<ContentFile>
<Title>St. Basil's</Title>
<Id>
/group/bb1438e3-0508-4ac7-a3fb-7872fc016e38/Moscow/St. Basil_s
</Id>
</ContentFile>
</ContentFolder>
</ContentFolder>

This folder structure is a bit too large to deal with a test data set, though it's good to see that everything is coming through.
Created a simpler structure in the Algebra course. Use this URL:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&wantAll=true&ids=310a0268-9c2b-4b5b-950a-689937088409

This has a structure of:

<ContentFolder>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>
<ContentFolder>
<Title>Folder-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/
</Id>
<ContentFolder>
<Title>Folder-1-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/
</Id>
<ContentFile>
<Title>Phase 0</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Id>
</ContentFile>
</ContentFolder>

<ContentFile>
<Title>Mister Sluggo</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif
</Id>
</ContentFile>
</ContentFolder>

<ContentFolder>
<Title>Folder-2</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/
</Id>
<ContentFile>
<Title>Phase 1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/p1.gif
</Id>
</ContentFile>
</ContentFolder>

<ContentFolder>
<Title>Folder-3</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-3/
</Id>
</ContentFolder>
</ContentFolder>

Three top level folders are provided: Folder-1, Folder-2, Folder-3. Folder-1 has a sub-folder called Folder-1-1 with a single file in it. Folder-1 also has a file in it. Folder-2 has a single file in it. Folder-3 is empty.

It looks like ContentHosting clusters resources and collections together within a given collection, though I'm not sure I can rely on that. they might be alphabetical. I'm going to add a few more files and see what happens.
Experiment seems to bear out that ContentHosting clusters resources and collections together, but I think I need to separte collections from resources within a given collection in order to put <ContentFolders> and <ContentFiles> tags around the set of folders and files. I really don't think this extra level in the XML is needed, but simple enough to conform to the existing syntax.

It should be possible to traverse each collection twice: first to handle sub-collections, then to handle resources contained in it. This will allow me to add the containing collection tags.

Made adjustments to include <ContentFolders> wrapper tags. Output is now:

<ContentFolders>
<ContentFolder>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>
<ContentFolders>
<ContentFolder>
<Title>Folder-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/
</Id>

<ContentFolders>
<ContentFolder>
<Title>Folder-1-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/
</Id>

<ContentFile>
<Title>Phase 0</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Id>
</ContentFile>
</ContentFolder>
</ContentFolders>

<ContentFile>
<Title>A simple circle</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/circle.gif
</Id>
</ContentFile>
<ContentFile>
<Title>Mister Sluggo</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif
</Id>
</ContentFile>
</ContentFolder>
</ContentFolders>

<ContentFolders>
<ContentFolder>
<Title>Folder-2</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/
</Id>

<ContentFile>
<Title>Phase 1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/p1.gif
</Id>
</ContentFile>
</ContentFolder>
</ContentFolders>

<ContentFolders>
<ContentFolder>
<Title>Folder-3</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-3/
</Id>
</ContentFolder>
</ContentFolders>
</ContentFolder>
</ContentFolders>

This looks right. Now I need to group files into <ContentFile> containment tags.
Broke out scan for resources separate from sub-collections. Ouput structure is now:

<ContentFolders>
<ContentFolder>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>

... sub-folder here

<ContentFiles>
<ContentFile>
<Title>A simple circle</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/circle.gif
</Id>
</ContentFile>
</ContentFiles> <- Wrong!
<ContentFiles> <- Wrong!
<ContentFile>
<Title>Mister Sluggo</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif
</Id>
</ContentFile>
</ContentFiles>

</ContentFolder>

...

</ContentFolders>

Output structure is not right. the circle.gif and slug.gif resources should be contained in a single <ContentFiles> tag.
Problem with an if test. Used = instead of ==. That's not good.

Structure looks right now:

<ContentFolders>
<ContentFolder>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>

<ContentFolders>
<ContentFolder>
<Title>Folder-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/
</Id>

<ContentFolders>
<ContentFolder>
<Title>Folder-1-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/
</Id>

<ContentFiles>
<ContentFile>
<Title>Phase 0</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Id>
</ContentFile>
</ContentFiles>
</ContentFolder>
</ContentFolders>

<ContentFiles>
<ContentFile>
<Title>A simple circle</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/circle.gif
</Id>
</ContentFile>

<ContentFile>
<Title>Mister Sluggo</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif
</Id>
</ContentFile>
</ContentFiles>
</ContentFolder>
</ContentFolders>

<ContentFolders>
<ContentFolder>
<Title>Folder-2</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/
</Id>

<ContentFiles>
<ContentFile>
<Title>Phase 1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/p1.gif
</Id>
</ContentFile>
</ContentFiles>
</ContentFolder>
</ContentFolders>

<ContentFolders>
<ContentFolder>
<Title>Folder-3</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-3/
</Id>
</ContentFolder>
</ContentFolders>
</ContentFolder>
</ContentFolders>

The circle.gif and slug.gif file entries are now contained in a single <ContentFiles> node.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Add additional folder information (done)
  • Add additional file information (done)
  • Filter folders by user access
  • Filter resources by user access
  • Announcements
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login request

Nov. 30, 2008
-------------

Started work on adding additional file information. The following tags should be included for all files:

<StorageSystem>DOS</StorageSystem>
<FileSystemEntryType>File</FileSystemEntryType>
<Title>STAPLES.GIF</Title>
<LinkName>My Name of Link to Image</LinkName>
<ModifiedDate Day="31" Hour24="21" Minute="12" Month="10" Second="45" Year="2008"/>
<Size>57707</Size>
<CourseId>_16_1</CourseId>
<Id>_110_1~_26_1</Id>
<ParentId>_110_1</ParentId>
<Url>http://demo73.classtop.com/courses/1/herbology101/content/_110_1/STAPLES.GIF</Url>
<IsAvailable>true</IsAvailable>
<DisplayMethod>Link</DisplayMethod>

After working out small problems, the data for a <ContentFile> now looks like this:

<ContentFile>
<DisplayMethod>Link</DisplayMethod>
<ParentId>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/
</ParentId>
<LinkName>Phase 0</LinkName>
<Size>281</Size>
<FileSystemEntryType>File</FileSystemEntryType>
<Url>
http://localhost:8080/access/content/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Url>
<IsAvailable>true</IsAvailable>
<Title>Phase 0</Title>
<CourseId>310a0268-9c2b-4b5b-950a-689937088409</CourseId>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Id>
<ModifiedDate Day="29" Hour24="11" Minute="3" Month="11" Second="34" Year="2008"/>
</ContentFile>

Hmm, missed StorageSystem. The actual storage system is pretty hidden in the system. I think "Sakai" will have to suffice.
This tag reads as:

<StorageSystem>Sakai</StorageSystem>

The value "Sakai" was chosen to reflect the use of the Sakai ContentHosting services as the underlying storage mechanism. Likely this is being used to determine how file name delimiters are used (slash vs. back slash). Since Sakai uses only identifiers, such delimiters do not corresopnd to an operating system.

All <ContentFolder> entries must have the following tags:

<Title>External Links</Title>
<ContentHandler>Folder</ContentHandler>
<CourseId>_16_1</CourseId>
<Id>_108_1</Id>
<ParentId>0</ParentId>
<PositionalOrder>7</PositionalOrder>

Folders that contains sub-folders have the following tags in addition to those above:

<AllowGuests>true</AllowGuests>
<AllowObservers>true</AllowObservers>
<IsAvailable>true</IsAvailable>
<ExternalLink></ExternalLink>
<ExternalLinkInNewWindow>false</ExternalLinkInNewWindow>
<TitleColor>000000</TitleColor>
<CreatedDate Day="31" Hour24="21" Minute="3" Month="10" Second="31" Year="2008"/>
<ModifiedDate Day="31" Hour24="21" Minute="18" Month="10" Second="20" Year="2008"/>
<LmsUrl>http://demo73.classtop.com/webapps/blackboard/content/listContentEditable.jsp?content_id=_108_1&amp;course_id=_16_1&amp;mode=quick</LmsUrl>
<WhatTheCrap>Folder</WhatTheCrap>
<CanContainFolders>true</CanContainFolders>
<IsTracked>false</IsTracked>

Added code. Here is the result for Folder-3, which has no sub-folders:

<ContentFolders>
<ContentFolder>
<ParentId>/group/310a0268-9c2b-4b5b-950a-689937088409/</ParentId>
<PositionalOrder/>
<ContentHandler>Folder</ContentHandler>
<Title>Folder-3</Title>
<CourseId>310a0268-9c2b-4b5b-950a-689937088409</CourseId>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-3/
</Id>
</ContentFolder>
</ContentFolders>
<ExternalLinkInNewWindow>false</ExternalLinkInNewWindow>
<PositionalOrder/>
<CanContainFolders>true</CanContainFolders>
<ContentHandler>Folder</ContentHandler>
<LmsUrl>
http://localhost:8080/access/content/group/310a0268-9c2b-4b5b-950a-689937088409/
</LmsUrl>
<TitleColor>000000</TitleColor>
<IsAvailable>true</IsAvailable>
<CourseId>310a0268-9c2b-4b5b-950a-689937088409</CourseId>
<ParentId>/group/</ParentId>
<AllowObservers>true</AllowObservers>
<WhatTheCrap>Folder</WhatTheCrap>
<ExternalLink/>
<IsTracked>false</IsTracked>
<AllowGuests>true</AllowGuests>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>
<ModifiedDate Day="28" Hour24="12" Minute="14" Month="10" Second="30" Year="2008"/>
<ModifiedDate Day="28" Hour24="12" Minute="14" Month="10" Second="30" Year="2008"/>

This was initially confusing (there is quite a bit of data present now). It seems that folder information is added to the <ContentFolder> tag after any sub-folders or ContentFiles are included. In the excerpt above, Folder-3 correctly has no additional data, just the basic folder information (because it has no sub-folders). This is followed by information for the root folder (Algebra_1_1_fall2008). So the XML is correct, thought it is getting very difficult to read.

All content information is now present in output.

Now we come to the hard part: filtering by user access to content. Sakai doesn't have access permissions for single items. In theory, a user can be excluded from accessing a particular collection in a course site. However, the Resources tool doesn't give this level of permission control to an instructor. The instructor can specify if an item is available only to members of this group or is publically accessible, and can specify the reveal/retract date.

In many ways, this greatly simplifies determination of access permissions. The following checks need to be made:

1. Determine which roles have "content.read" permission.
2. Determine which roles have "content.revise.any" or "content.revise.own" permissions.
3. Determine which roles have "content.hidden" permission.
4. Determine if the time falls outside of reveal/retract dates.

The complete set of functions for the Resource Tool includes:

content.new
content.read
content.revise.any
content.revise.own
content.delete.any
content.delete.own
content.all.groups
content.hidden

Some exploration of functions will be needed.
The worksite "Algebra_1_1_fall2008" has an AuthzGroup of "310a0268-9c2b-4b5b-950a-689937088409", which is the same as the site id.

Extended debug mode to emit site and content information. Use this URL:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&debug=t&siteid=310a0268-9c2b-4b5b-950a-689937088409

Functions are registered with the global FunctionManager and can be accessed as a complete list or with a prefix.

Some questions to answer:

1. Is the user a member of this site?
2. Is content publically accessible?
3. Can the user read a particular content item?
4. Can the user revise a particular content item?
5. Is a particular content time outside of reveal/retract dates?

Dec. 1, 2008
------------

Changed status of slug.gif to "hidden" in Algebra course. Visible to mark, but not to betty.
Use thse URLs to check on specific items:

http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&debug=t&siteid=310a0268-9c2b-4b5b-950a-689937088409&content=/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif
http://localhost:8080/cfgateway?action=GetCourseList&fbId=745478915&roles=*&debug=t&siteid=310a0268-9c2b-4b5b-950a-689937088409&content=/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif

Added code to doDebug() to check for a content item reference and see if the supplied user has read access to it.

Site and Content Information
Site id is: 310a0268-9c2b-4b5b-950a-689937088409
Site title is: Algebra_1_1_fall2008

The following roles are defined for this site:
Student
Instructor
Teaching Assistant

The following content functions are registered (globally):
content.new
content.read
content.revise.any
content.revise.own
content.delete.any
content.delete.own
content.all.groups
content.hidden

FaceBook User Id is: 99999999
Sakai User Id is: mark
Enterprise Id is: mark
User mark is a member of Algebra_1_1_fall2008
User mark is not able to read /group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif

SecurityService.unlock() failed for slug.gif, though it should have succeeded.

In BaseContentService, the following function is used to check a permission:

boolean unlockCheck(String lock, String id);

It, in turn, uses getReference() to convert an id to a reference. This 'might' be the problem.
getReference() returns getAccessPoint(true) + id; <- this actually means that the access point is NOT pre-pended.
getAccessPoint references ServerConfigurationService.getAccessUrl(). Where does that live? component/api.

The SU tool loves in tool/tool.

Tried ContentHostingService.allowGetResource() agains the active users (coursefeedgateway).

coursefeedgateway is able to read /group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif via CHS.
User mark is not able to read /group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/slug.gif

Had a look at the SU tool. The key section of the code seems to be this:

// while keeping the official usage session under the real user id, swicth over everything else to be the SU'ed user
// Modeled on UsageSession's logout() and login()
Session sakaiSession = SessionManager.getCurrentSession();

// logout - clear, but do not invalidate, preserve the usage session's current session
Vector saveAttributes = new Vector();
saveAttributes.add(UsageSessionService.USAGE_SESSION_KEY);
sakaiSession.clearExcept(saveAttributes);

// login - set the user id and eid into session, and refresh this user's authz information
sakaiSession.setUserId(validatedUserId);
sakaiSession.setUserEid(validatedUserEid);
AuthzGroupService.refreshUser(validatedUserId);

This would seem to switch over to the specified user, but it's not clear how it is switched back. Hopefully, it is only for the duration of the request.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Filter folders by user access
  • Filter resources by user access
  • Announcements
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login request

Dec. 2, 1008
------------

Cut a new release of Sakai CourseFeed and installed it on demo71. Port 8080 is still not open which means I can't access it remotely.
Tried to create some new course sites via project sites, but instructor and student roles are missing. Need to create an academic term entry in DB to allow course sites to be created. Ideally, AT tool is needed.

Dec. 8, 2008
------------

Time to experiment on the SU approach to filtering content.

Created a GetContentInfo request purely as a test of the new approach. Use this URL to test:

http://localhost:8080/cfgateway?action=GetContentInfo&fbId=745478915&siteId=310a0268-9c2b-4b5b-950a-689937088409

or

http://localhost:8080/cfgateway?action=GetContentInfo&fbId=99999999&siteId=310a0268-9c2b-4b5b-950a-689937088409

Commented out extra data in folders and files to simplify the result XML. I need to be able to check for folders/files present. The user "mark" is the instructor of Algebra and has access to all folders and files. The user "betty" is a student and doesn't have access to "slug.gif" or "folder-2". This latter case is the test to see if the SU approach will work for the CourseFeed gateway.

Prior to inserting SU code, XML generated for betty fails, since it includes:

<ContentFolders>
<ContentFolder>
<ContentFiles>
<ContentFile>
<Title>Phase 1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/p1.gif
</Id>
<ModifiedDate Day="29" Hour24="11" Minute="3" Month="11" Second="53" Year="2008"/>
</ContentFile>
</ContentFiles>
<Title>Folder-2</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-2/
</Id>
</ContentFolder>
</ContentFolders>

The SU approach does a partial logout on the current user (cfgateway), carefully saving it's USAGE_SESSION_KEY. Then, it logs in as the secondary user by setting the uid, and eid in the session. A call to the authzGroupService to refresh the user is the critical step, I think.

Added code and ran it. Unfortunately, betty still has access to files she shouldn't.
I added code after the SU to get the current user, which is correctly identified as "betty". I would think that ContentHosting would pick up on the current user and get permissions for her accordingly, but that doesn't seem to be the case.

Printf's document the current user per REST request:

First request:

        • CourseFeedServlet - Initial user is: coursefeedgateway
        • CourseFeedServlet - Initial Session user is: coursefeedgateway
        • CourseFeedServlet - SU user is: betty
        • CourseFeedServlet - SY session user is: betty

Second requst:

        • CourseFeedServlet - Initial user is: betty
        • CourseFeedServlet - Initial Session user is: betty
        • CourseFeedServlet - SU user is: betty
        • CourseFeedServlet - SY session user is: betty

Notice that the session and current user have switched over to betty from coursefeed. Obviously some sort of hand shaking, session id, or restoration of user will be needed. However, the content hosting service is still not detecting betty as the current user.

I noticed a method called isAvailable() in the CHS that seems to check if a resource is hidden. This might do it for me. However, it is not present in the cover. Had to shift code to use an instance of CHS instead. CHS.checkResource() also has potential.

Some good news:

        • CourseFeedServlet - Initial user is: coursefeedgateway
        • CourseFeedServlet - Initial Session user is: coursefeedgateway
        • CourseFeedServlet - File slug.gif is available to coursefeedgateway
        • CourseFeedServlet - SU user is: betty
        • CourseFeedServlet - SY session user is: betty
        • CourseFeedServlet - File slug.gif is not available to betty

there is a clear shift in availablily after the SU. This could do what I need it to do. Now I need to put it into the GetCourseList code. Adde code. Same results as above in console. XML output (simplified) is:

<CourseList>
<Courses>
<ContentFolders>
<ContentFolder>
<ContentFolders>
<ContentFolder>
<ContentFolders>
<ContentFolder>
<ContentFiles>
<ContentFile>
<Title>Phase 0</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/p0.gif
</Id>
<ModifiedDate Day="29" Hour24="11" Minute="3" Month="11" Second="34" Year="2008"/>
</ContentFile>
</ContentFiles>
<Title>Folder-1-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/Folder-1-1/
</Id>
</ContentFolder>
</ContentFolders>

<ContentFiles>
<ContentFile>
<Title>A simple circle</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/circle.gif
</Id>
<ModifiedDate Day="29" Hour24="11" Minute="13" Month="11" Second="34" Year="2008"/>
</ContentFile>
</ContentFiles>

<Title>Folder-1</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-1/
</Id>
<ModifiedDate Day="29" Hour24="11" Minute="0" Month="11" Second="5" Year="2008"/>
<ModifiedDate Day="29" Hour24="11" Minute="0" Month="11" Second="5" Year="2008"/>
</ContentFolder>
</ContentFolders>

<ContentFolders>
<ContentFolder>
<Title>Folder-3</Title>
<Id>
/group/310a0268-9c2b-4b5b-950a-689937088409/Folder-3/
</Id>
</ContentFolder>
</ContentFolders>
<Title>Algebra_1_1_fall2008</Title>
<Id>/group/310a0268-9c2b-4b5b-950a-689937088409/</Id>
<ModifiedDate Day="28" Hour24="12" Minute="14" Month="10" Second="30" Year="2008"/>
<ModifiedDate Day="28" Hour24="12" Minute="14" Month="10" Second="30" Year="2008"/>
</ContentFolder>
</ContentFolders>
</Courses>
</CourseList>

The folder-1 and folder-3 are present and folder-3 is hidden (correct). file circle.gif is present, but slug.gif is hidden (correct).
Checked it against user "mark". Console output is:

        • CourseFeedServlet - Initial user is: betty
        • CourseFeedServlet - Initial Session user is: betty
        • CourseFeedServlet - File slug.gif is not available to betty
        • CourseFeedServlet - SU user is: mark
        • CourseFeedServlet - SY session user is: mark
        • CourseFeedServlet - File slug.gif is available to mark

and all content that should be visible is. Note that it doesn't seem to matter if I restore the current user to "coursefeedgateway" or not. This needs more testing, however.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Restore cfgateway user after SU?
  • Check on affects of SU on other functions. Still need to log in?
  • Announcements
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login request done

Dec. 11, 2008
-------------

Implemented a login command by re-using code from web service login. Use the following URL to test:

http://localhost:8080/cfgateway?action=Login&id=coursefeedgateway&pw=classtop

Seems to work correctly. A session id is returned:

08aa6f31-2381-4e77-8c48-b9835a3976f3

Turning on debugging flag results in:

Request URL: http://localhost:8080/cfgateway
Request URI: /cfgateway
Path Info: /

Query Parameters:
debug has a value of t
pw has a value of classtop
action has a value of Login
id has a value of coursefeedgateway

Cookies sent:
1: JSESSIONID – 59f8befd-9add-4fa4-8d7c-f98f4517f9ab.localhost

Although the session id doesn't seem to match the JSESSIONID, various REST requests seem to be working. Debug flag works. GetSiteInfo works. GetCourseRoster works.
I'm going to assume that Login is working ok, though more testing might be needed.

GetContentInfo indicates that the initial user is null.

http://localhost:8080/cfgateway?action=GetContentInfo&fbId=745478915&siteId=310a0268-9c2b-4b5b-950a-689937088409
http://localhost:8080/cfgateway?action=GetContentInfo&fbId=99999999&siteId=310a0268-9c2b-4b5b-950a-689937088409

Here are the results of two successive GetContentInfo requests:

        • CourseFeedServlet - Initial user is: null (should be coursefeedgateway)
        • CourseFeedServlet - Initial Session user is: null
        • CourseFeedServlet - File slug.gif is not available to null
        • CourseFeedServlet - SU user is: betty
        • CourseFeedServlet - SY session user is: betty
        • CourseFeedServlet - File slug.gif is not available to betty
          WARN: refreshUser: cannot find eid for user: (2008-12-11 18:12:22,877 http-8080
          -Processor24_org.sakaiproject.authz.impl.BaseAuthzGroupService)
        • CourseFeedServlet - User on exit is: null
        • CourseFeedServlet - Initial user is: null (should be coursefeedgateway)
        • CourseFeedServlet - Initial Session user is: null
        • CourseFeedServlet - File slug.gif is not available to null
        • CourseFeedServlet - SU user is: mark
        • CourseFeedServlet - SY session user is: mark
        • CourseFeedServlet - File slug.gif is available to mark
          WARN: refreshUser: cannot find eid for user: (2008-12-11 18:12:58,721 http-8080
          -Processor25_org.sakaiproject.authz.impl.BaseAuthzGroupService)
        • CourseFeedServlet - User on exit is: null

I think this is a login problem, perhaps with the session manager.
Using session.setEid() didn't do the trick. User still comes up as null.
Setting the user uid didn't help, either. I think I may need to update the JSESSIONID
Added AuthzGroupService.refreshUser(userId). That also didn't work.

Added a printf for req.getRemoteHost().

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Check on affects of SU on other functions. Still need to log in?
  • Announcements
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login
  • Write to the JSESSIONID cookie.

Dec. 12, 2008
-------------

Located an old copy of the academic-term tool that allows an admin to create (and manage) academic terms using the Sakai Course Management Service. I'll need to update the maven projects to work with maven 2.0, but I expect it to run as is.

Updating the JSESSIONID requires knowing the client host name. I think I can extract this from the request.

Saving JSESSIONID as a cookie in the response doesn't seem to stick. I suspect it is being overwritten by the SessionManager during the final response.

New Session Id: 3bb72924-48c5-49df-810b-c2bb325a5735

JSESSIONID – 3ea3c1a9-9701-4a6c-8a1e-e9f7650e3067.localhost

It's going to have to wait.

Announcements have the following form:

<Announcements>
<Announcement>
<Title>Test announcement subject</Title>
<CourseId>_16_1</CourseId>
<CreatorUserId>_23_1</CreatorUserId>
<CreatorUserName>Crabapple, Mary</CreatorUserName>
<RestrictStartDate Day="31" Hour24="21" Minute="0" Month="10" Second="0" Year="2008"/>
<RestrictEndDate Day="1" Hour24="22" Minute="0" Month="11" Second="0" Year="2009"/>
<Id>_39_1</Id>
<LmsUrl>http://demo73.classtop.com/bin/common/announcement.pl?action=LIST&amp;render_type=EDITABLE&amp;time=all&amp;context=course&amp;course_id=_16_1</LmsUrl>
<IsAvailable>true</IsAvailable>
<IsPermanent>false</IsPermanent>
<BodyFormat>Html</BodyFormat>
<Body>Test announcement body. <strong>This sentence is bold.</strong></Body>
</Announcement>
</Announcements>

I have created two announcements in the accounting course that can be used as test data.

To add announcments, I need to first get an announcement channel:

AnnouncementService.getAnnouncementChannel(String ref);

Question is, what is the reference? It's likely to be the site id, in this case, but may need a prefix of some kind. The link for a specific message in the Announcements tool is:

itemReference=/announcement/msg/bb1438e3-0508-4ac7-a3fb-7872fc016e38/main/59de9834-aa09-4a7a-80cf-35cedc75711c

This gives us some clues. The MessageService will provide a list of channels, but expects a context. Some experimentation is called for. Use this URL:

http://localhost:8080/cfgateway?action=GetAnncInfo&fbId=99999999&siteId=bb1438e3-0508-4ac7-a3fb-7872fc016e38

The following text is emitted:

Get Announcement Information
User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38

Using the site id as a context, I get:

Get Announcement Information
User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38
Number of channels found: 1

Added code to interate over the channel ids and display them:

User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38
1 channels were found:
Channel main

getChannel failed when I tried to feed it the channel id recieved. It needs a reference context, likely the following:

/announcement/msg/site-id/channel-id/msg-id

Let's try it. Nope, that didn't work. Sigh. Hunting and fishing for things that SHOULD be documented (but usually are not). This is why third party developers have so much trouble with Sakai - and also why they bring in people like me.

Tried altering the reference string:

User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38
1 channels were found:
Channel main
Unable to get channel reference /announcement/bb1438e3-0508-4ac7-a3fb-7872fc016e38/main

Sometimes speculation pays off. Changed the reference to /announcement/channel/ with the following results:

User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38
1 channels were found:
Channel main
Channel reference /announcement/channel/bb1438e3-0508-4ac7-a3fb-7872fc016e38/main has 2 messages.

This successfully accessed the announcement channel. Now we can drill down into the messages, which is what we are REALLY interested in.

User is: betty
Site is: bb1438e3-0508-4ac7-a3fb-7872fc016e38
1 channels were found:
Channel main
Channel reference /announcement/channel/bb1438e3-0508-4ac7-a3fb-7872fc016e38/main has 2 messages:
Message 59de9834-aa09-4a7a-80cf-35cedc75711c has a subject of Second Test
Message b1f389b0-6390-411a-a1fb-c20595824f5d has a subject of Test Announcement

This is exactly what I need to display messages. This can now be migrated over to doRestGetAnnouncementInfo() and returned as XML. Commented out addContentInfo() to simplify results reporting.

Use this URL:
http://localhost:8080/cfgateway?action=GetCourseList&fbId=99999999&roles=*&wantAll=true&ids=bb1438e3-0508-4ac7-a3fb-7872fc016e38

Information generated (at this time):

<CourseList>
<Courses>
<Course>
...
<Id>bb1438e3-0508-4ac7-a3fb-7872fc016e38</Id>
<CourseDisplayId>ACCT_100_1_fall2008</CourseDisplayId>
<Announcements>
<Announcements>
<Body><p>this is plain text</p></Body>
<Title>Second Test</Title>
<Id>59de9834-aa09-4a7a-80cf-35cedc75711c</Id>
</Announcements>
<Announcements>
<Body><p><strong>This text is in bold.</strong></p></Body>
<Title>Test Announcement</Title>
<Id>b1f389b0-6390-411a-a1fb-c20595824f5d</Id>
</Announcements>
</Announcements>
</Course>
</Courses>
</CourseList>

Got the message tag wrong. Should be "announcement". Fixed.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Check on affects of SU on other functions. Still need to log in?
  • Announcements
    Flesh out additional data.
    Filter by user access.
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login
  • Write to the JSESSIONID cookie.

Dec. 13, 2008
-------------

The following data should be present in each announcement:

<Title>Test announcement subject</Title> done
<Id>announcement id</Id> done
<Body>escaped body announcement</Body> needs to be escaped

<CourseId>course id</CourseId>
<IsAvailable>true</IsAvailable>
<IsPermanent>false</IsPermanent>
<BodyFormat>Html</BodyFormat>
<LmsUrl>access url</LmsUrl>

<CreatorUserId>creator id</CreatorUserId>
<CreatorUserName>creator sort name</CreatorUserName>
<RestrictStartDate Day="31" Hour24="21" Minute="0" Month="10" Second="0" Year="2008"/>
<RestrictEndDate Day="1" Hour24="22" Minute="0" Month="11" Second="0" Year="2009"/>

Added a bit of code to dump list of resource properties associated with a message with the following results:

        • CourseFeedServlet - message property: retractDate
        • CourseFeedServlet - message property: releaseDate

These are needed, but I expected to see a user id as well.
Added code to get the retract and release dates, but an error resulted:

        • CourseFeedServlet - message property: retractDate
        • CourseFeedServlet - message property: releaseDate
        • CourseFeedServlet - message creator: null
        • CourseFeedServlet: Error when getting announcement data.
          org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: An invalid or illegal XML character is specified.
          at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.createElement(CoreDocumentImpl.java:626)
          at org.sakaiproject.coursefeed.servlet.CourseFeedServlet.addAnnouncementInfo(CourseFeedServlet.java:1191)

Ok, the release and retract dates are stored strings, but in an unusal format:

        • CourseFeedServlet - release date: 20081212180000000
        • CourseFeedServlet - retract date: 20091211180000000

20081212180000000 translates at Dec 12, 2008 1:00 pm in the Announcement tool. 20081212 is obviously the date part, but 180000000 doesn't seem to fit 1:00pm (13:00). There are also from and for entries in the table.
18 00 00 000

I changed one of the times to 2:30 pm and this was saved as 20081212 193000000
The date/times seems to be saved in GMT. The following code:

String releaseDate = props.getProperty("releaseDate");
String retractDate = props.getProperty("retractDate");
Time releaseTime = TimeService.newTimeGmt(releaseDate);
Time retractTime = TimeService.newTimeGmt(retractDate);

System.out.println ("**** CourseFeedServlet - release date: "+releaseTime.getDisplay());
System.out.println ("**** CourseFeedServlet - retract date: "+retractTime.getDisplay());

Generated these results:

        • CourseFeedServlet - release date: Dec 12, 2008 12:05 am
        • CourseFeedServlet - retract date: Dec 11, 2009 1:00 pm
        • CourseFeedServlet - release date: Dec 12, 2008 2:30 pm
        • CourseFeedServlet - retract date: Dec 11, 2009 1:00 pm

These match up with the announcement tool.
Data now seems to be correct:

<Announcement>
<CreatorUserId>admin</CreatorUserId>
<BodyFormat>Html</BodyFormat>
<LmsUrl>
http://localhost:8080/access/announcement/msg/bb1438e3-0508-4ac7-a3fb-7872fc016e38/main/b1f389b0-6390-411a-a1fb-c20595824f5d
</LmsUrl>
<IsAvailable>true</IsAvailable>
<Body><p><strong>This text is in bold.</strong></p></Body>
<Title>Test Announcement</Title>
<IsPermanent>false</IsPermanent>
<CreatorUserName>Administrator, Sakai</CreatorUserName>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>b1f389b0-6390-411a-a1fb-c20595824f5d</Id>
<RestrictStartDate Day="12" Hour24="0" Minute="5" Month="12" Second="0" Year="2008"/>
<RestrictEndDate Day="11" Hour24="13" Minute="0" Month="12" Second="0" Year="2009"/>
</Announcement>

Announcements need to be filtered by access permissions, then it will be done.

Todo:
CourseFeed Application

  • Re-direct success and fail URLs in Sakai CourseFeed application.
  • Remove extra links from Sakai CourseFeed application.
    GetCourseList - wantAll flag
  • Check on affects of SU on other functions. Still need to log in?
  • Announcements
    Filter by user access. (done)
  • GradeableItems
    GetUserInfo
  • Check SakaiPerson for additional personal information.
    SendCourseEmail
  • Implement the SendCourseEmail request.
    Login
  • Write to the JSESSIONID cookie.

Dec.15, 2008
------------

Fixed a bug when an announcement is hidden. It might not have release and retract dates causing a null pointer error.
Adde changeUser() calls to doRestGetCourseList(). This should filter information by user access for content, announcements, and (when implemented) grades.

Grade data includes:

<GradeableItems>
<GradeableItem>
<Title>Weighted Total</Title>
<CourseId>_16_1</CourseId>
<ColumnOrder>1</ColumnOrder>
<Id>_32_1</Id>
<IsAvailable>true</IsAvailable>
<PointsPossible>0.0</PointsPossible>
<NumberOfScores>0</NumberOfScores>
<Weight>0.0</Weight>
<StudentCount>3</StudentCount>
<PositionalOrder>1</PositionalOrder>
<LmsUrl>http://demo73.classtop.com/webapps/gradebook/do/instructor/itemGrades?course_id=_16_1&amp;outcomeDefinitionId=_32_1&amp;receiptUrl=null&amp;method=modify</LmsUrl>
</GradeableItem>
</GradeableItems>

Added code to generate GradeableItems with the following data:

Id
Title
CourseId
PointsPossible

Got a permissions violation on GradebookService.getAssignments(). This is understandable, but a bit of a problem.
I don't think this needs to be filtered like access to content and announcements. Disabled changeUser() and tried again.
Assignment ids are a bit strange. They are Longs and need to be converted to strings.
Generates the following data:

<GradeableItems>
<GradeableItem>
<Title>First Test</Title>
<PointsPossible>10.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>3</Id>
</GradeableItem>
<GradeableItem>
<Title>Final Exam</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>4</Id>
</GradeableItem>
<GradeableItem>
<Title>Open source paper</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>5</Id>
</GradeableItem>
<GradeableItem>
<Title>Open source project</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>6</Id>
</GradeableItem>
<GradeableItem>
<Title>TEST05</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>7</Id>
</GradeableItem>
<GradeableItem>
<Title>TEST06</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>8</Id>
</GradeableItem>
<GradeableItem>
<Title>TEST08</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>9</Id>
</GradeableItem>
<GradeableItem>
<Title>Test01</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>10</Id>
</GradeableItem>
<GradeableItem>
<Title>Test02</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>11</Id>
</GradeableItem>
<GradeableItem>
<Title>Test03</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>12</Id>
</GradeableItem>
<GradeableItem>
<Title>Test04</Title>
<PointsPossible>100.0</PointsPossible>
<CourseId>bb1438e3-0508-4ac7-a3fb-7872fc016e38</CourseId>
<Id>13</Id>
</GradeableItem>
</GradeableItems>

The following assignment information is not available:

ColumnOrder
NumberOfScores
Weight
StudentCount
PositionalOrder
LmsUrl

Added a bit of code to handle IsAvailable, since it's available in Assignment.
Now I need to figure out how to SU for content and assignements, but not for GradeableItems. Once that's fixed, we can release this baby with most functionality in place.

Fixed code by adding instance variables for CourseFeed User and FaceBook User that can be accessed deeper in the GetCourseInfo response. Re-enabled all information fields. Tested it, works.

Cut a new build of Sakai CourseFeed and install it on demo71.

Sakai has been shifted to a different port on demo71. It should be accessible via the following URL

http://demo71.classtop.com:8090/portal/

Added student1 to Physics course as "access" - this site was originally created as a project site and is missing the student and instructor roles. The following URL can be used to see a list of site accessible by student1:

http://demo71.classtop.com:8090/cfgateway?action=GetCourseList&fbId=888888888&roles=*&wantAll=false&ids=*

This URL shows additional information:

http://demo71.classtop.com:8090/cfgateway?action=GetCourseList&fbId=888888888&roles=*&wantAll=true&ids=*