blog (development code)
authortoyomoyo <toyomoyo>
Fri, 10 Mar 2006 06:53:01 +0000 (06:53 +0000)
committertoyomoyo <toyomoyo>
Fri, 10 Mar 2006 06:53:01 +0000 (06:53 +0000)
21 files changed:
blog/README.txt [new file with mode: 0644]
blog/TODO.txt [new file with mode: 0644]
blog/blog.js [new file with mode: 0644]
blog/blogpage.php [new file with mode: 0644]
blog/class.BlogEntry.php [new file with mode: 0755]
blog/class.BlogFilter.php [new file with mode: 0755]
blog/class.BlogInfo.php [new file with mode: 0755]
blog/class.HttpClient.php [new file with mode: 0644]
blog/config.html [new file with mode: 0644]
blog/edit.html [new file with mode: 0755]
blog/edit.php [new file with mode: 0755]
blog/footer.php [new file with mode: 0644]
blog/header.php [new file with mode: 0755]
blog/index.php [new file with mode: 0755]
blog/lib.php [new file with mode: 0755]
blog/preferences.html [new file with mode: 0755]
blog/preferences.php [new file with mode: 0755]
blog/set_session_vars.php [new file with mode: 0644]
blog/tags.html [new file with mode: 0755]
blog/tags.php [new file with mode: 0755]
blog/version.php [new file with mode: 0644]

diff --git a/blog/README.txt b/blog/README.txt
new file mode 100644 (file)
index 0000000..c3b53c4
--- /dev/null
@@ -0,0 +1,102 @@
+=================================================
+MOODLE BLOG
+=================================================
+
+This moodle module is based on the Simplog blogging system.
+You can find more information on simplog at
+http://www.simplog.net / http://www.simplog.org
+Moodle integration work by Daryl Hawes.
+
+=================================================
+TESTERS WANTED
+=================================================
+
+This module is currently in the alpha testing phase.
+
+I would appreciate feedback on any interface or functionality issues 
+that you have. Please post your comments on moodle.org. Click "Free Support"
+from the main page and then click "Blogs" to join the discussion.
+
+Special thanks to these testers deserving of mention:
+Tom Murdock
+WP1
+Dan Marsden
+
+=================================================
+TESTING
+=================================================
+There is online help available by adding the "Blog Menu" block and then clicking the Blog Help link. The online help is quickly getting out of date and could use some help - if you
+make any modifications please send them my way for inclusion (or just commit them if you have cvs access.)
+
+Once you have the files in place, either by copying the full easy distribution or by copying the files from cvs over an existing 
+installation, you'll need to initialize moodle and complete the installation.
+
+-- Admin your server --
+Now that the moodle blog components are in place you'll want to update your database to support it. Visit yoursite.com/moodle/admin/ to 
+run the database updates.
+
+When the updates has been completed click the continue link to continue on to the variables setup page. 
+When you're done configuring basic moodle settings then visit the admin area again and click on the 'Blog" link to admin site wide blog 
+settings.
+
+-- Access blogs --
+To get started blogging you can stop by your blog index page at yoursite.com/moodle/blog/ or you can create a new course and use the 
+Blog Menu block. Each user will need to click the "enable your blog" link to be able to add entries.
+Note that when you add an entry the entry form recognizes whether you came from a course page. 
+If you came from a course page the entry becomes associated with that course. 
+If you did not get to the add and entry form from a course then your entry will be considered a general entry and not associated with 
+any course.
+
+Extra Credit:
+Dan Marsden has some good tips on getting a high level of
+error and warning output when testing development code. Perhaps you can
+try various settings to give us extra feedback. Note that you would also want to
+have the moodle debugging option turned on in the moodle admin configuration page.
+
+"There are 3 things I have set differently (as far as I can remember!)
+    in the Moodle install process I set the debuging to be "on"
+    in php.ini file,Ê
+        error_reporting = E_ALL
+    and in my browser [Ed. I assume he is on a windows machine likely using some form of IE],
+        internet options > advanced 
+        under the browsing section
+        display a notification about every script error selected
+        disable Script debugging unselected."
+                        --Dan Marsden, 2004
+
+
+=================================================
+README CONTENTS ADAPTED FROM SIMPLOG
+=================================================
+Welcome to Moodle Blog (based on Simplog)!
+
+Thanks for downloading this software while it in its testing phase.
+Simplog is free software, released under the GPL:
+http://www.gnu.org/copyleft/gpl.html GNU Public License
+
+Simplog was created to provide people with an easy way to create and maintain their own personal or a community weblog.  With the increasing number of people creating on-line journals, there is a need for a tool that allows them a quick and easy way to share their 
+lives with the rest of the world.
+
+Moodle Blog is designed first and foremost with personal expression in mind. It is not
+implemented as an assignment or course module within Moodle. Rather, it is incorporated
+directly and is independent of any course. Each user has the ability to create one
+personal blog where they can post their personal reflections. A 'course blog' is just
+an aggregate of entries from individual user blogs that have been flagged as being
+'associated' with that course.
+
+Daryl Hawes
+
+
+
+Relevant portion of Simplog.net notes (from CREDITS.txt file removed from distribution):
+
+The Magpie RSS Parser and Atom Parser codebase courtesy of http://magpierss.sf.net
+
+Trackback codebase contributed by Dougal Campbell - http://dougal.gunters.org
+Pingback code based on pingback implementation for b2 - http://www.cafelog.com
+
+Simplog development team:
+f-bomb - http://www.webhack.com
+jbuberel - http://www.buberel.org
\ No newline at end of file
diff --git a/blog/TODO.txt b/blog/TODO.txt
new file mode 100644 (file)
index 0000000..f96f264
--- /dev/null
@@ -0,0 +1,274 @@
+ * @uses parseIni() <-- note for phpdoc, you can use @uses function() notation! great for deprecated functions and functions that are wrappers for functions with different args, etc. Also use the @deprecated keyword on some of those.
+
+
+MEETING NOTES:
+
+Need to convert api.php, archive.php and index.php, blogEntry.class and anything else over to use the new uniquehash as the blog entry identifier rather than blogid/postid combination.
+
+EDIT: Did I include the bit about messaging? If a student is moderated and they change their publish state they should be prompted to enter a message as to what they are changing the publish state for (teacher review, show the class because it's done, etc) and then a moodle message is generated to the teacher.
+
+New course formatting - tabbed interface which has "Course", "Blog", "etc". (Done)
+In the blog tab of course use table class to place User picture/name/last blog entry mod date/latest blog entries (see more link) sorted by default by most recent entry. Need "sort by" in this section.
+
+User profile tabs - add a new tab "My Blog" (if you come to the user profile from a course you see course posts, from site you see site posts - though a drop down selection would be nice)
+
+Categories should have the same tinyurl hash as a unique id for backup and restore. (similar to the way that the quiz module currently handles quiz questions. eg?  www.mysite.com.63AB6VD. I'm thinking md5($ownerid.$CFG->wwwroot.$categoryid))
+
+Grading feature will have to be available early on. Maybe add a third table for blog grades/courseid. The teacher viewing the course entries would have the ability to set the grades for those entries. Teacher would set the scales for their course. Note: Grades in most places in moodle are done using an integer, if negative then refers to scale id.
+
+Anonymous comments are not to be allowed at all. remove the ability for guest users to post comments to blog entries entirely. Remove comments on posts entirely. Instead of comments the options should be to "Send a private message" or blog a reference ("blog this!"), or add a forum post referencing the entry.
+no anonymous comments, trackback and pingback optional, little finger with string tied icon to "blog this post", internal trackback style.
+Trackback/pingback from remote sites should not be allowed at all I guess (even on publicly published entries) since they're a form of anonymous comment. 
+Trackbacks should not use blog id/post id - they should use a unique field like quiz does with a "tinyurl" or random number/hash which will better allow backup and restore.
+
+Default blog template 'moodle' (for layout) needs to look exactly like forum posts, including extra space under user picture. Modify 'moodle' template!
+
+In the People block perhaps add a "This user's blog". List all of the class faces with their most recent blog entries, sorted by most recent entries.
+
+"everything I have ever said to this person" would be a useful bit of information.
+
+Post table:
+  - id
+  - author (userid)
+  - reader (userid) 
+  - courseid
+  - moduleid
+  - coursemoduleid
+  - groupid
+  - subject (text)
+  - summary (text)
+  - content (text)
+  - uniquehash  (similar to the way that the quiz module currently handles quiz questions. eg?  www.mysite.com.63AB6VD)
+  check to see if the int id for blog api will accepts an md5(string) value. use marsedit and hack api.php to test. (-UPDATE- Looks good! Blogger api uses string values for blogid and postid)
+
+ post->id == blog_entries->id
+ post->author == blog_entries->userid
+ post->reader == ?
+ post->courseid == blog_entries->courseid
+ post->moduleid == ?
+ post->coursemoduleid == ?
+ post->groupid ==== blog_entries->groupid (unused by blog so far)
+ post->subject == blog_entries->title
+ post->summary == blog_entries->body
+ post->content == blog_entries->extendedbody
+ post->uniquehash  == md5($post->author.$CFG->wwwroot.$post->id) (fair?)
+(similar to the way that the quiz module currently handles quiz questions. eg?  www.mysite.com.63AB6VD)
+
+
+New course formatting - tabbed interface which has "Course", "Blog", "etc". (Done)
+no comments, remove comments from blog entries. (Done)
+Remove the use of blogid completely. The blogid IS the userid - just refer the the moodle userid. 1 user == 1 blog, period. - DONE (api.php still uses 'blogid' in a few places internally)
+For blog entries change the publish to text of No one (draft) to 'publish to: "just for myself"' - DONE
+Remove class.BlogServer.php file completely. - DONE
+By removing blog_list and blog_acl we now have to store user preferences for their blog title and tagline, useextendedbody as well as potentially theme choice. It appears typeid was never used. - DONE
+The blog_acl table and all references to it can be completly removed. we have wikis for shared documents and other tools. - DONE
+RSS lib has gotten some work recently. Moodle still uses RSS2 only, no selection, so remove rss 0.9 and Atom options entirely. - DONE
+From Daryl: Remove all search references. If forum uses the POST table in the future then searching forum vs. blog entries becomes irrelevent. - DONE
+
+
+NOTE: blog preferences have (finally) been moved to user preferences
+set_user_preference($name, $value, $otheruser=NULL);
+get_user_preferences($name=NULL, $default=NULL, $userid=NULL);
+preference keys for blog:
+blogtitle
+blogtagline
+bloguseextendedbody
+blogtheme
+bloglastmodified
+
+
+__________
+REGULAR TODO:
+
+                                               MOODLE BLOG TODO
+=================================================
+NEEDS TESTING
+ (NOTE: If you decide to test these items please
+send your detailed observations to dhawes@mac.com
+so they can either be cleared out or added back
+to active bug tracking. Thanks!)
+=================================================
+
+----- New blog moderation/permissions model added -----
+
+The default is current permission model
+Current permission model can be defined as
+    1) All users have the same publish abilities - no moderation, no restrictions
+    2) When posting an entry users can publish as a draft, to teachers in a given course, to all users in a given course, to all site members or to the world.
+
+I have now added an admin toggle to enable/disable blog moderation.
+Blog moderation is now defined as
+    1) Admin users have current permission model
+    2) Teachers posting an entry associated with a course that they teach in can publish to any option.
+    3) all other posts must be associated with a course! When moderation is on there are no "personal" blog entries.
+    4) all other posts are sent to draft by default. student users can publish to teacher only (or draft of course). 
+    5) Teachers will see posts "published to teacher" by students in their class (later perhaps also notified by moodle message - right now they will see the entries on the blog index page only)
+    6) Only a teacher can promote an entry from draft/teacher to class view, site view, or world view
+    7) Once promoted/published the student can no longer edit that entry. To edit the teacher will have to first demote the entry back to draft.
+    8 ) Batch publishing now available to all users on index page - Teachers' interface  has a "publish all" option as well as a per message publish option
+
+    
+=================================================
+ACTIVE WORK / TO BE COMPLETED
+   (bug fixes and other things targeted for first release)
+=================================================
+
+¥rss_client - during upgrade check for fopen() (or whatever is required) and disable the block if it cannot function on the server? How to properly introspect this functionality?
+
+¥ Elimiate admin.php file - move one remaining function into category block
+
+¥ Calendar Block Bug: Cal is showing link to dates with blog entries which are not found in the current blog! It should only show links to dates that have entries which the user can see given their blogFilter constraints. (Odd - in testing on my local site I cannot reproduce this error).
+
+¥ Martin suggested that the "Enable blog" step should occur on first attempt at new entry - eliminating one step. I'm not thoroughly convinced that this is the best, it needs more discussion. If you enable the blog just because they click "post new entry" then you are adding extra blogs for the curious who may not use it after first investigation. The best compromise is to show the'learning text' on the edit form itself and enable the blog when they submit their first entry, but the edit form doesn't lend itself well to this approach and the edit form would be pretty far down the page.
+(One possible solution is to add a bool column to the blog_list table enabled/disabled. If the user hits the edit page and they have no blog then create one in the db for them and use that blogid for the remainder of the page. If their blog is disabled (default) then add a link to the top of the page suggesting that they read the help file on "About Blogs" before posting.)
+
+¥ Martin makes a good point about eliminating jargon for the end user experience. Look through blocks and links and comb out overly cryptic wording.
+
+¥ Continue work on help pages for trackback and pingback. Explain what they are and why you would want to send or not send them from a post. (posted discussion topic requesting assistance with this text on using moodle blog forum)
+
+¥ phase out the use of BlogEntry->entryCategoryIds since BlogEntry->entryCategories now contains both the id and name in a single associative array.
+
+RSS (server): feeds NOW support args of blogid=# or catid=#. As we integrate further with moodle we may want a courseid=# option. This applies to rss, rss2 and atom.php. Courseid may not be useful except on courses which allow guest access. 
+I do not know for sure that rss feeds can be authenticated. The current rss feeds do not do any form of authentication. How would a news reader client handle authentication of the feed? Page re: authenticated feeds: http://labs.silverorange.com/archives/2003/july/privaterss
+
+¥ On edit.php indent the send trackback option and make it dependent on pingback being enabled
+for reference on how to do this see  code for "when adding a Resource" (I'm not positive that tb/pb are quite this neatly related with tb dependant on pb being enabled. I believe that tb could be enabled without pb and it will work. This requires more research and better help file explanations of feature functionality. It might seem appropriate to simply allow trackback as it seems to be built on top of pb, but I believe pb is supported because some remote servers do not support tb.)
+
+¥ SECURITY - ADMIN CONFIG OPTION - I'm thinking lately that it might be nice during install time (and as an admin blog config option) to present the user with a choice - "use blogs" <yes/no> -  that way any "security" concerns are negated since it can be completely disabled.
+
+¥ Check to see if guest users who have joined the course with an enrollment key can post comments when anon comments disabled/enabled - maybe extra setting in config - "allow guests who enter with enrolment key to post comments"
+
+¥ Convert archive.php into view.php which will be a permalink page for viewing single entries only.
+
+=================================================
+ONGOING / STYLE
+=================================================
+¥ Examine database usage and attempt to optimize: (note from Jon: you can say "EXPLAIN (your query here)" and MySQL will tell you what it's doing to execute the query for example, how many rows it will have to consider, what indexes it will be using and stuff)
+
+¥ if you're worried about memory bloat - just don't keep the objects around any longer than you need to -  e.g. after done with it call "unset($page)"
+
+¥ Add phpdoc comments to files, classes and functions.
+
+¥ SQL: Convert more sql calls to moodle functions rather than raw sql.
+
+¥ LOCALIZATION: Review code occasionally to localize any hard coded text
+
+¥ MOODLE INTEGRATION/CLEANUP: enforce integer values before using them in db calls:     
+// Make sure that the GET variables are correct
+    $day = intval($_GET['cal_d']);
+
+¥ MOODLE INTEGRATION/CLEANUP: Use p() when showing vars in html forms
+<input name="block_online_users_timetosee" type="text" size="5" value="<?php if(isset($CFG->block_online_users_timetosee)) {p($CFG->block_online_users_timetosee);} else {p(5);} ?>"
+actually p(0 and s() just pass the argument though strip_chars and htmlspecialchars to remove < > special characters - making sure it's just text.
+
+¥ from Jon: $motto = "always use empty() UNLESS there is a specific reason not to"
+and the specific reasons may be two:
+1) you care to differentiate between NULL and ''
+2) you want to see if an object exists before starting to pull out its properties
+
+=======================================================================
+FEATURE REQUESTS (not targeted for first release, order means nothing)
+=======================================================================
+¥ http://wordpress.org/about/shots/1.5/moderation.png - comment moderation for the blog owner themselves
+
+¥ RSS 2.0's enclosure capabilities would be a nice addition for podcasting.
+
+¥ From Martin's bug regarding blogging:
+"3) Blog entries appear as usual, and can have a discussion attached to them for feedback and comments. Each entry can also have a "grade", which is the average of a number of ratings. Normally this might just be the teacher, but it could be the whole class rating each other's entries."
+
+¥ find a way to moderate based on the category and course rather than the site. Would allow one teacher to moderate their own course entries while another chooses not to
+
+¥ RSS_CLIENT: 
+define('MAGPIE_CACHE_FRESH_ONLY', false); //should be exposed as an admin config option - if true then it will not display stale cache contents when remote feed cannot be refreshed.
+
+¥ PINGBACK/TRACKBACK: PingBacks and TrackBacks should be logged in moodle logs as they are saved/executed.
+
+¥ Handle deletion of user
+   BlogServer class has function deleteBlogById($blogid). This is not currently called by any blog code. It might be a good function to be used by moodle. However, moodle's delete user simply marks the user as deleted - allowing the user to be easily reactivated. Should we be checking for ($user->deleted != 1) before displaying blog information?
+   
+¥ Possibly re-enable weblogs.com RPC option
+//below is a section removed from edit.php.
+<?php
+if($CFG->blog_useweblog_rpc) {
+echo "<input type=checkbox name=weblogrpc value=\"1\">Send update notice to weblogs.com<br>(may cause longer load time)&nbsp;";
+}?>
+
+¥ DH - RSS - All rss/atom scripts should use a blogFilter with arguments passed in to be used in loading correct blogEntries. Either the news page or a new function in BlogFilter should build the cache filename. Create a new page that has full drop down menu options to select a specific feed type (filter news feeds by category, user, date to start with)
+
+¥ RSS: Single page where users can go to access rss links (xml image and link) which are available to them on the moodle server - instead of only being available in blocks. When working with our own published feeds add links for administrator to "Validate" remote feed. Should integrate with new news page that lists all available local feeds. ( http://feeds.archive.org/validator/check?url=<BLOGRSSURL>) (NEWS/RSS: add new index_news.php file. should list all possible feeds for the site. including blogids, catids and moodle links. At the top should be "My RSS Feeds" for logged in users to quickly find their own links. RSS Feeds side block and the user admin section should have links to this new page.)
+
+¥ API.PHP: Add support for media upload api metaweblog call. Would allow a user to post a blog entry complete with pictures. "Finally, the ability of students to include pictures and other media is very useful." Should target this ASAP. Right now I'm waiting for moodle to have a comprehensive file management system that would allow a student to upload their own files to the server.
+
+¥ Add entry/comment view counters
+    COUNTERS: Add view counters to simplog tables (or create blog_log table). How many times has the full body of an entry been viewed, how many times have comments on an entry been viewed? (member vs. guest views should be counted) Should be a new template markup option --showguestviews-- --showmemberviews-- --showtotalviews--. 
+
+
+INDEX_MORE.PHP: Column headers should be linked to allow column sorting. Clicking a column header would cause the sort order to be changed to be by that column and paging would be reset back to start=0.
+
+TRACKBACK/PINGBACK: Add ability to blacklist remote URLs so that specific incoming tb/pbs can be rejected on a site level
+
+=================================================
+ADDITIONAL NOTES / UNCATEGORIZED NOTES
+=================================================
+
+CALENDAR: It would be nice if Blog entries that have been marked 'published' appear on the calendar as EVENTS! The first publish it'll appear as 'entry created', changing status and then republishing will make it show on the calendar as 'entry status modified'. When an entry is edited it'll show on the calendar as 'entry modified'. These event entries should not be editable by the user.
+From email with Jon P. :
+    Is it possible to create an event in the calendar programmatically
+that
+       1) is NOT tied to a courseid, an event for the user, not for a course
+they are in
+       2) is not editable by the user
+
+"OK, your question has two parts and a two-sided answer. First of all, you
+could add fields to mdl_event that make the recognition of a fifth type of
+event possible, and change the code to make use of that. But this is a
+solution I would advise against, not to mention that you would have to make
+the changes mentioned for the "other solution" below.
+
+Another thing you could do is fiddle with the calendar_edit_event_allowed()
+function (or something like that). I think it's located at about the end
+of calendar/view.php[...]. That one decides whether you can edit the event or not,
+so your best bet is to add code there to achieve your desired effect. For
+example, add another column to mdl_event and make that function return
+false if it's set to 1.
+
+A third solution just came to mind. You can always change the link 
+for the edit icon. That's what Martin did for activity events; they don't
+allow you to change the event details, but take you to the corresponding
+activity which "controls" the event."
+
+MOODLE INTEGRATION/CALENDAR: Convert existing simplog calendar code to use Moodle calendar. This is terribly involved unfortunately.
+
+BLOG_ENTRIES: blog entries should track their modification date info and note the most recent update in the listing. Would be a nice addition if it also listed all modifications dates in the full single entry view (archive.php). Users who make updates should be able to enter a comment about what the update was. Now that we have publish states perhaps a new table should have a list of all modifications made to non-draft entries. when entries were returned to draft status, republished, edited, etc.
+Additionally, perhaps a modification can only ADD extra text rather than editing the original post (this would be a site wide setting and would make published blog posts more moodle like)
+
+WEBLOGRPC.php: use as a template to create a blog entry publishing system for other blogs? Refer to other open source blog systems to see if others do this already. They might have useful code to use as a reference or starting point.
+LONG TERM:(Tom note)Teachers down the road might want to attach scales and grades to individual blog instances, entries, or categories...
+
+
+
+Posted By: vik 
+Date: 9:56 AM 31-Mar-2004
+
+Having used blogs at my school for a few months, I'd like to add these observations. 
+
+We used blogs in two ways; one for personal reflection, the other for text-responses. In the latter, the students were directed by questions from the teacher, and as I understand, had much the same functionality of the journal module. The personal blogs on the other hand, have completely different requirements:
+
+Firstly, students need ownership of the blog. This means the blog would have to be assigned to the *student*, not any course. This might be 'worked around' by using form groups or year groups or similar as courses, but ownership is one of the concepts we identified that makes blogs so appealing to students. 
+
+Further to this, I think that having the structure and layout of the blog customisable is very beneficial. How possible would this be (to have the students modify html and css for their own instances of such a module)? 
+
+______________________________
+
+
+RSS:
+Notes from: http://www.learningcircuits.org/2004/may2004/0405_trends.htm
+
+And what about other technologies? Could RSS be integrated with other new tools, such as the ones described in ÒWe LearningÓ, parts I and II? Consider
+¥ a social networking tool that sends you a feed informing you of the new people who just joined the network
+¥ an expert management system that uses feeds to tell you when a new expert is added to the system in your area of interest, or when an expert has created a new document you might be interested in
+¥ collaborative workspaces that use feeds to bring in information people in the group need to complete a project
+¥ a problem-based RSS feed integrated with a social networking or expert management tool in which you could subscribe to a problem, for example Òreluctant learners,Ó and then receive updates whenever someone writes on that topic.
+
+   ¥ RSS Server: Create RSS feed of member joins for each course.
\ No newline at end of file
diff --git a/blog/blog.js b/blog/blog.js
new file mode 100644 (file)
index 0000000..abcf6e3
--- /dev/null
@@ -0,0 +1,52 @@
+<!--//
+function del(baseurl, id, userid) {
+
+       if(confirm("Do you really want to delete that blog entry?")) {
+               document.location = baseurl+"/edit.php?act=del&postid="+id+"&userid="+userid;
+       }       
+}
+
+function openPrev() { 
+    //dh - added try{}catch{} statements to allow the function to continue along even
+    //if some of the elements it is expecting are not present in the original form
+
+    preview = window.open('', 'preview', 'width=640,height=480,scrollbars=yes,status=yes,resizable=yes');
+       document.prev.elements['format'].value = document.entry.format.selectedIndex;
+       document.prev.elements['etitle'].value = document.entry.elements['etitle'].value;
+//    alert('title = '+document.entry.elements['etitle'].value);
+
+    if (window.frames.length > 0) {
+        // editor is loaded
+        document.prev.elements['body'].value = document.all ? frames[0].document.body.innerHTML : frames[1].document.body.innerHTML;
+        try {
+            document.prev.elements['extendedbody'].value = document.all ? frames[1].document.body.innerHTML : frames[0].document.body.innerHTML;
+        } catch(e) {
+            ; //ignore failure
+        }
+    } else {
+        // standard webforms
+        document.prev.elements['body'].value = document.entry.elements['body'].value;
+        try {
+            document.prev.elements['extendedbody'].value = document.entry.elements['extendedbody'].value;
+        } catch(e) {
+            ; //ignore failure
+        }
+    }
+
+    try {
+        var sourceSelect = document.entry.elements['categoryid[]'];
+        var targetSelect = document.prev.elements['categoryid[]'];            
+
+        for (i=0; i < sourceSelect.length; i++) {
+            if (sourceSelect.options[i].selected == true) {
+                targetSelect.options[i].selected = true;
+            } else {
+                targetSelect.options[i].selected = false;
+            }
+        }
+    } catch(e) {
+        ; //ignore failure
+    }
+    document.prev.submit();
+}
+//-->
\ No newline at end of file
diff --git a/blog/blogpage.php b/blog/blogpage.php
new file mode 100644 (file)
index 0000000..c52f6bb
--- /dev/null
@@ -0,0 +1,179 @@
+<?php  // $Id$
+
+/**
+* Definition of blog page type.
+ */
+define('PAGE_BLOG_VIEW', 'blog-view');
+
+// Blog class derived from moodle's page class
+class page_blog extends page_base {
+
+    var $editing = false;
+    var $bloginfo = NULL;
+    var $courserecord = NULL;
+    var $courseid = NULL;
+    
+    // Mandatory; should return our identifier.
+    function get_type() {
+        global $CFG;
+        require_once($CFG->dirroot .'/blog/lib.php');
+        return PAGE_BLOG_VIEW;
+    }
+    
+    // we have no format type, use 'blog'
+    //I think it's a bug, but if this is left the default NULL value then pages can
+    //fail to load completely
+    function get_format_name() {
+        global $CFG;
+        require_once($CFG->dirroot .'/blog/lib.php');
+        return PAGE_BLOG_VIEW;
+    }
+
+    // Do any validation of the officially recognized bits of the data and forward to parent.
+    // Do NOT load up "expensive" resouces (e.g. SQL data) here!
+    function init_quick($data) {
+        parent::init_quick($data);
+        if (empty($data->pageid)) {
+            //if no pageid then the user is viewing a collection of blog entries
+            $this->id = 0; //set blog id to 0
+        }
+    }
+    
+    // Here you should load up all heavy-duty data for your page. Basically everything that
+    // does not NEED to be loaded for the class to make basic decisions should NOT be loaded
+    // in init_quick() and instead deferred here. Of course this function had better recognize
+    // $this->full_init_done to prevent wasteful multiple-time data retrieval.
+    function init_full() {
+        if ($this->full_init_done) {
+            return;
+        }
+        // I need to determine how best to utilize this function. Most init
+        // is already done before we get here in blogFilter and blogInfo
+        $this->bloginfo =& new BlogInfo($this->id);
+        if ($this->courseid == 0 || $this->courseid == 1 || !is_numeric($this->courseid) ) {
+            $this->courseid = '';
+            $courserecord = NULL;
+        } else {
+            if (! ($courserecord = get_record('course', 'id', $this->courseid)) ) {
+                error( 'You are tring to view an invalid course. Id: ('. $this->courseid .')' );
+            }
+        }
+        $this->full_init_done = true;
+    }    
+
+    // For this test page, only admins are going to be allowed editing (for simplicity).
+    function user_allowed_editing() {
+        if (isadmin() || ((isset($this->bloginfo) && blog_user_has_rights($this->bloginfo))) || ($this->courseid != '' && isteacher($this->courseid)) ) {
+            return true;
+        }
+        return false;
+    }
+
+    // Also, admins are considered to have "always on" editing (I wanted to avoid duplicating
+    // the code that turns editing on/off here; you can roll your own or copy course/view.php).
+    function user_is_editing() {
+        if (isadmin() || ((isset($this->bloginfo) && blog_user_has_rights($this->bloginfo))) || ($this->courseid != '' && isteacher($this->courseid)) ) {
+            global $SESSION;
+            if (empty($SESSION->blog_editing_enabled)) {
+                $SESSION->blog_editing_enabled = false;
+            }
+            $this->editing = $SESSION->blog_editing_enabled;
+            return $this->editing;
+        }
+        return false;
+    }
+
+    //over-ride parent method's print_header because blog already passes more than just the title along
+    function print_header($pageTitle='', $pageHeading='', $pageNavigation='', $pageFocus='', $pageMeta='') {
+        global $USER;
+        $this->init_full();
+        $extraheader = '';
+        if (!empty($USER) && !empty($USER->id)) {
+            $extraheader = $this->get_extra_header_string();
+        }
+        print_header($pageTitle, $pageHeading, $pageNavigation, $pageFocus, $pageMeta, true, $extraheader );
+    }
+    
+    // This should point to the script that displays us
+    function url_get_path() {
+        global $CFG;
+        return $CFG->wwwroot .'/blog/index.php';
+    }
+
+    function url_get_parameters() {
+        $array = array();
+        if (!$this->full_init_done) {
+            $array['userid'] = $this->id;
+            return $array;
+        }
+
+        //I should likely just bring blog filter in here and return 
+        //the output of a filter method like get_params
+        //instead let's simply return the userid and courseid        
+        $array['userid'] = $this->id;
+        if (!empty($this->courseid)) {
+            $array['courseid'] = $this->courseid;
+        }
+        return $array;
+    }
+
+
+    // Having defined all identifiers we need, here we declare which block positions we are
+    // going to support.
+    function blocks_get_positions() {
+        return array(BLOCK_POS_LEFT, BLOCK_POS_RIGHT);
+    }
+    
+    // When a new block is created in this page, which position should it go to?
+    function blocks_default_position() {
+        return BLOCK_POS_RIGHT;
+    }
+
+    // When we are creating a new page, use the data at your disposal to provide a textual representation of the
+    // blocks that are going to get added to this new page. Delimit block names with commas (,) and use double
+    // colons (:) to delimit between block positions in the page. See blocks_get_positions() for additional info.
+    function blocks_get_default() {
+        global $CFG;
+        
+        $this->init_full();
+        
+        // It's a normal blog page
+        if (!empty($CFG->{'defaultblocks_'. $this->get_type()})) {
+            $blocknames = $CFG->{'defaultblocks_'. $this->get_type()};
+        } else {
+            /// Failsafe - in case nothing was defined.
+            $blocknames = 'admin,calendar_month,blog_member_list:login,online_users,blog_menu,blog_categories,blog_recent_entries,blog_news_feeds';
+        }
+        
+        return $blocknames;
+    }    
+
+    // And finally, a little block move logic. Given a block's previous position and where
+    // we want to move it to, return its new position. Pretty self-documenting.
+    function blocks_move_position(&$instance, $move) {
+        if ($instance->position == BLOCK_POS_LEFT && $move == BLOCK_MOVE_RIGHT) {
+            return BLOCK_POS_RIGHT;
+        } else if ($instance->position == BLOCK_POS_RIGHT && $move == BLOCK_MOVE_LEFT) {
+            return BLOCK_POS_LEFT;
+        }
+        return $instance->position;
+    }
+
+    /////////// Blog page specific functions
+    function get_extra_header_string() {
+        global $SESSION, $CFG, $USER;
+        $editformstring = '';        
+        if (!empty($SESSION->blog_editing_enabled) && ($SESSION->blog_editing_enabled)) {
+            $editingString = get_string('turneditingoff');
+        } else {
+            $editingString = get_string('turneditingon');
+        }
+        $editformstring = '<form target="'. $CFG->framename .'" method="get" action="'. $CFG->wwwroot .'/blog/set_session_vars.php">'.
+            '<input type="hidden" name="referrer" value="'. me() .'" />'.
+            '<input type="hidden" name="var" value="showediting" />'.
+            '<input type="submit" value="'. $editingString .'" /></form>';
+       
+        return $editformstring;        
+    }    
+}
+?>
\ No newline at end of file
diff --git a/blog/class.BlogEntry.php b/blog/class.BlogEntry.php
new file mode 100755 (executable)
index 0000000..0322385
--- /dev/null
@@ -0,0 +1,817 @@
+<?php // $Id$
+
+/*******************************************************************
+ * Class to represent a blog entry.
+ *
+ * @copyright Copyright (C) 2003, Jason Buberel
+ * @author Jason Buberel jason@buberel.org {@link http://www.buberel.org/}
+ * @version  $Id$
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package blog
+ ******************************************************************/
+
+global $CFG;
+include_once($CFG->dirroot .'/blog/lib.php');
+
+/**
+ *
+ * This class represents a single entry in a blog. Normally, you wouldn't
+ * need to call the constructor directly...you would instead make use of
+ * a BlogInfo object's entry retrieval methods in order to get an instance
+ * of BlogEntry.
+ *
+ * To create a new blogEntry object use BlogInfo's insert_blog_entry() function.
+ *
+ * @todo Ultimately this class might to be expanded to include a factory
+ * method for the creation of new BlogEntries (->create()).
+ * Better yet:
+ fix constructor to not have to take a record set - should be empty and use setters
+ verify that everything has getters/setters
+ make sure that both the ->save and ->update function operate properly on an entry that does not yet exist!
+ this way one could:
+ $newEntry = new BlogEntry();
+ $newEntry->set_title('a title');
+ $newEntry->set_body('a post here');
+ $newEntry->set_userid(2);
+ $newEntry->save();
+ */
+class BlogEntry {
+    // member variables
+    var $entryId; // post.id
+    var $entryBody; // post.summary
+    var $entryExtendedBody; // post.content
+    var $entryTitle; // post.subject
+    var $entryKarma; // post.rating
+    var $entryFormat; // post.format
+    var $entryuserid; // post.author    
+    var $entryGroupId; // post.groupid
+    var $entryCourseId; // post.courseid
+    var $entryPublishState; // post.publishstate
+    var $entryAuthorName; // blog_users.name
+    var $entryAuthorEmail; // blog_users.email
+    
+    //Daryl Hawes note: entryCategoryIds should be phased out as entryCategories is an
+    //associative array of $id => $name elements
+    var $entryCategoryIds = array(); // post.id -> blog_categories_entries.categoryid
+    var $entryCategories = array(); // post.id -> blog_categories_entries.categoryid -> blog_categories.catname
+    
+    var $entryLastModified; // last modification date post.lastmodified
+    var $formattedEntryLastModified; // post.lastmodified
+    var $entryCreated; // creation date post.created
+    var $formattedEntryCreated; // post.created
+
+    /**
+     * Class constructor that will build a new instance of the object
+     * when given a reference to an object that contains
+     * all of the keys from a row in the post table
+     * Daryl Hawes note: constructor should be changed not to have to take in a database row!
+     *
+     * @param object $entrydetails reference to an object that contains
+     *                              all of the keys from a row in the post table
+     * @uses $CFG
+     * @uses $db
+     * @todo finish documenting this constructor
+     */
+    function BlogEntry(&$entrydetails) {
+        global $db, $CFG;
+//        print_object($entrydetails); //debug
+
+        $this->entryId = $entrydetails->id;
+
+        if (!empty($entrydetails->categoryid)) {
+            if (is_array($entrydetails->categoryid)) {
+                $this->entryCategoryIds = $entrydetails->categoryid;
+            } else {
+                $this->entryCategoryIds = array($entrydetails->categoryid);
+            }
+        } else {
+            // load up all categories that this entry is associated with
+            // cannot use moodle's get_records() here because this table does not conform well enough
+            $sql = 'SELECT * FROM '. $CFG->prefix .'blog_categories_entries WHERE entryid='. $this->entryId;
+            if($rs = $db->Execute($sql)) {
+                while (!$rs->EOF) {
+                    $this->entryCategoryIds[] = $rs->fields['categoryid'];
+                    $rs->MoveNext();            
+                }
+            }
+        }
+        $this->entryCategoryIds = array_unique($this->entryCategoryIds);
+//        print "Debug: entryId: $this->entryId"; //debug
+//        print_object($this->entryCategoryIds); //debug
+        
+        $this->entryBody = ereg_replace('<tick>', "'", stripslashes_safe($entrydetails->summary));
+        if (isset($entrydetails->extendedbody)) {
+            $this->entryExtendedBody = ereg_replace('<tick>', "'", stripslashes_safe($entrydetails->extendedbody));
+        } else {
+            $this->entryExtendedBody = '';
+        }
+        
+        $strftimedaydatetime = get_string('strftimedaydatetime');
+        $this->entryLastModified = $entrydetails->lastmodified;
+        $this->formattedEntryLastModified = userdate($this->entryLastModified, $strftimedaydatetime);
+        $this->entryCreated = $entrydetails->created;
+        $this->formattedEntryCreated = userdate($this->entryCreated, $strftimedaydatetime);
+        
+        $this->entryuserid = $entrydetails->userid;
+        
+
+        //added stripslashes_safe here for rss feeds. Will this conflict anywhere?
+        
+        
+        $this->entryTitle = ereg_replace('<tick>', "'", stripslashes_safe($entrydetails->subject));   //subject, not title!
+        
+        $this->entryFormat = $entrydetails->format;
+
+        //Daryl Hawes additions: course and group ids
+        if (isset($entrydetails->groupid) ) {
+            $this->entryGroupId = $entrydetails->groupid;
+        }
+        if (isset($entrydetails->courseid) ) {
+            $this->entryCourseId = $entrydetails->courseid;
+        }
+
+        if (isset($entrydetails->publishstate) ) {
+            $this->entryPublishState = $entrydetails->publishstate;
+        } else {
+            $this->entryPublishState = 'draft';
+        }
+
+        // need to get the email address of the author.
+        if (! $rs = get_record('user', 'id', $this->entryuserid)) {
+                error('Could not find user '. $this->entryuserid ."\n"); //Daryl Hawes note: needs localization
+                die;
+        }
+        $this->entryAuthorName = $rs->firstname .' '. $rs->lastname;
+        //need to make sure that email is actually just a link to our email sending page.
+        $this->entryAuthorEmail = $rs->email;
+
+        // then each category
+        if (!empty($this->entryCategoryIds)) {
+            foreach ($this->entryCategoryIds as $categoryid) {
+                if (! $currcat = get_record('blog_categories', 'id', $categoryid)) {
+                    print 'Could not find category id '. $categoryid ."\n";
+                    $this->entryCategories[$categoryid] = '';
+                } else {
+                    $this->entryCategories[$categoryid] = $currcat->catname;
+                }
+            }
+        }
+    }
+
+    /**
+     * delete this entry
+     *
+     * @return bool Returns true on successful deletion
+     */
+    function delete() {
+        if (! delete_records('post', 'userid', $this->entryuserid, 'id', $this->entryId)) {
+                print 'Could not find blog entry matching author with user id '. $this->entryuserid .'  and entry with id '. $this->entryId ."\n";
+                return false;
+        }
+        return true;
+    }
+    
+    /**
+     * get_formatted_karma_link
+     *
+     * @return string If allowed a link to set karma for this entry will be returned
+     * @uses $USER
+     * @uses $CFG
+     */
+    function get_formatted_karma_link() {
+        global $USER, $CFG;
+        $str = '';
+        if (!empty($CFG->blog_ratename)) {
+            $str .= $CFG->blog_ratename .': ';
+        }
+        $str .= $this->entryKarma;
+        if ( !isguest() && blog_isLoggedIn()) {
+            $str .= ' ( <a href="'. $CFG->wwwroot .'/blog/karma.php?op=add&amp;userid='. $this->entryuserid .'&amp;postid='. $this->entryId .'">+</a> / <a href="'. $CFG->wwwroot .'/blog/karma.php?op=sub&amp;userid='. $this->entryuserid .'&amp;postid='. $this->entryId .'">-</a> )';
+        }
+        return $str;
+    }
+
+    /**
+     * get_formatted_category_link
+     *
+     * @return string unordered list of categories this entry is associated with
+     * @uses $CFG
+     */
+    function get_formatted_category_link() {
+        global $CFG;
+        $returnstring = '<span class="post-category">';
+        
+        if (!empty($this->entryCategoryIds)) {
+            $count = count($this->entryCategoryIds);
+            foreach ($this->entryCategoryIds as $categoryid) {
+                $returnstring .= '<a href="'. $CFG->wwwroot .'/blog/index.php?user='. $this->entryuserid .'&amp;categoryid='. $categoryid .'">'. $this->entryCategories[$categoryid] .'</a>';
+                $count--;
+                if ($count != 0) {
+                    $returnstring .= ',&nbsp;';
+                }
+                $returnstring .= "\n";
+            }
+        }
+
+        return $returnstring.'</span>' . "\n";
+    }
+    
+    
+    /**
+     * get_formatted_course_link
+     *
+     * @return string Returns and unordered list of courses that this entry is associated with
+     * @uses $CFG
+     */
+    function get_formatted_course_link() {
+        global $CFG;
+        $returnstring = '<span class="post-course">';
+        $courseid = $this->entryCourseId;
+        if ( !empty($courseid) && !($courseid == 0 || $courseid == '' || ! is_numeric($courseid) )) {
+            if ($course = get_record('course', 'id', $courseid, '', '', '', '', 'fullname')) {
+                $returnstring .= '<a href="'. $CFG->wwwroot .'/course/view.php?id='. $courseid .'">'. $course->fullname .'</a>' . "\n";
+            }
+        }
+    
+        return $returnstring.'</span>' . "\n";
+    }
+
+    /**
+     * get_formatted_entry_link
+     *
+     * @return string Permalink URL wrapped in an HTML link
+     */
+    function get_formatted_entry_link() {
+    
+        // removed the word 'permalink' and replaced with 'Read More' to
+        // further eliminate jargon from moodle blog
+        // Daryl Hawes note: must localize this line now
+        $str = '<a href="'. $this->get_entryurl() .'">Read More</a>';
+        return $str;
+
+    }
+
+    /*
+    * get_simple_entry_link - Just the link, with no extra html. 
+    *
+    * @return string Returns just a URL with no HTML.
+    * (Daryl Hawes note: this function moved to class.Blogentry from lib.php)
+    */
+    function get_simple_entry_link() {
+    
+        $str = htmlspecialchars( $this->get_entryurl() );    
+        return $str;
+    
+    }
+    
+    /**
+     * get_blog_this_URL added by Daryl Hawes for moodle integration
+     *
+     * @param bool $showImage If true then the return string is an HTML image tag
+     *                          If false then the return string is an HTML text link
+     * @uses $CFG
+     * @return string An HTML image tag or text link depending upon $showImage argument
+     */
+    function get_blog_this_URL($showImage=false) {
+        $str = '';
+        global $CFG;
+        //ensure user is logged in and that they have a blog to edit
+        if ( !isguest() && blog_isLoggedIn() ) {
+            $blogThisString = '';
+            if ($showImage) {
+                $blogThisString = '<img src="'. $CFG->pixpath .'/blog/blog.gif" alt="'. get_string('blogthis', 'blog');
+                $blogThisString .= '!" title="'. get_string('blogthis', 'blog') .'!" border="0" align="middle" />';
+            } else {
+                $blogThisString = get_string('blogthis', 'blog');
+            }
+            if (!$showImage) { 
+                $str .= '('; 
+            }
+            $str .= '<a href="'. $this->get_entryblogthisurl() .'">'. $blogThisString .'</a>';
+            if (!$showImage) { 
+                $str .= ')'; 
+            }
+        }
+        return $str;
+    }
+
+    /**
+     * get_formatted_edit_URL added by Daryl Hawes for moodle integration
+     * An empty string is returned if the user is a guest, the user is not logged in,
+     * or the user is not currently editing their blog page (turn editing on button)
+     * we will only show edit link if the entry is in draft status or the user is an admin
+     * note: teacher should not be allowed to edit or delete - only demote back to draft
+     *
+     * @param bool $showImage If false a text link is printed. If true a linked edit icon is printed.
+     * @uses $USER
+     * @uses $CFG
+     * @todo get_formatted_delete_URL and get_formatted_edit_URL should be merged into a single function
+     */
+    function get_formatted_edit_URL($showImage=false) {
+        global $USER, $CFG;
+        $str = '';
+
+        if ( !isguest() && blog_isLoggedIn() && blog_isediting() && blog_is_blog_admin($this->entryuserid) 
+             && (!$CFG->blog_enable_moderation || isadmin() || $blogEntry->entryPublishState == 'draft') ) {            
+            $str = '<div class="blogedit">';
+
+            //check if user is in blog's acl
+            //instead of creating a new BlogInfo object might a BlogInfo pointer in BlogEntry constructor be better? Does php have singleton objects? if not then a bloginfo reference as an argument to the constructor of BlogEntry would be a good idea. (The only problem here is in pages with multiple bloginfo objects represented - aggregate pages.)
+            $bloginfo = new BlogInfo($this->entryuserid);
+            //if so then show them an edit link
+            if (blog_user_has_rights($bloginfo)) {
+                $editString = '';
+                if ($showImage) {
+                    $editString = '<img src="'. $CFG->pixpath .'/t/edit.gif" alt="'. get_string('edit');
+                    $editString .= '" title="'. get_string('edit') .'" align="absmiddle" height="16" width="16" border="0" />';
+                } else {
+                    $editString = get_string('edit');
+                }
+                if (!$showImage) { 
+                    $str .= '('; 
+                }
+                $str .= '<a title="'. get_string('edit') .'" href="'. $this->get_entryediturl() .'">'. $editString .'</a>';
+                if (!$showImage) { 
+                    $str .= ')'; 
+                }
+            }
+            $str .= '</div>';
+            unset($blogInfo); //clean up after ourselves        
+        }
+        return $str;
+    }
+    
+    /**
+     * get_formatted_delete_URL added by Daryl Hawes for moodle integration
+     * An empty string is returned if the user is a guest, the user is not logged in,
+     * or the user is not currently editing their blog page (turn editing on button)
+     * we will only show edit link if the entry is in draft status or the user is an admin
+     * note: teacher should not be allowed to edit or delete - only demote back to draft
+     *
+     * @uses $USER
+     * @uses $CFG
+     * @param bool $showImage If false a text link is printed. If true a linked delete icon is printed.
+     * @todo get_formatted_delete_URL and get_formatted_edit_URL should be merged into a single function
+     */
+    function get_formatted_delete_URL($showImage=false) {
+        global $USER, $CFG;
+        $str = '';
+        
+        if ( !isguest() && blog_isLoggedIn() && blog_isediting() && blog_is_blog_admin($this->entryuserid)
+             && (!$CFG->blog_enable_moderation || isadmin() || $blogEntry->entryPublishState == 'draft') ) {
+            
+            $str = '<div class="blogdelete">';
+            
+            //check if user is in blog's acl
+            //instead of creating a new BlogInfo object might a BlogInfo pointer in BlogEntry constructor be better? Does php have singleton objects? if not then a bloginfo reference as an argument to the constructor of BlogEntry would be a good idea.
+            $bloginfo =& new BlogInfo($this->entryuserid);
+            //if so then show them an edit link
+            if (blog_user_has_rights($bloginfo)) {
+                $deleteString = '';
+                if ($showImage) {
+                    $deleteString = '<img src="'. $CFG->pixpath .'/t/delete.gif" alt="'. get_string('delete');
+                    $deleteString .= '" title="'. get_string('delete') .'" align="absmiddle" border="0" />';
+                } else {
+                    $deleteString = get_string('delete');
+                }
+                if (!$showImage) {
+                    $str .= '(';
+                }
+                $str .= '<a title="'. get_string('delete') .'" href="'. $this->get_entrydeleteurl() .'">'. $deleteString .'</a>';
+                if (!$showImage) { 
+                    $str .= ')'; 
+                }
+            }
+            $str .= '</div>';
+            unset($blogInfo); //clean up after ourselves
+        }
+        return $str;
+    }
+
+    /**
+     * get_formatted_entry_body
+     * getter for ->entryBody.
+     *
+     * @uses $CFG
+     * @return string Entry body/summary run through moodle's format_text formatter and 
+     *                  with slashes stripped from database entry
+     */
+    function get_formatted_entry_body() {
+        global $CFG;
+        include_once($CFG->libdir .'/weblib.php');
+        if ( isset($this->entryFormat) ) {
+            return format_text($this->entryBody, $this->entryFormat);
+        }
+        return stripslashes_safe($this->entryBody);
+    }
+    
+    /**
+     * get_unformatted_entry_body
+     * getter for ->entryBody
+     *
+     * @return string Entry body/summary - raw string from database
+     */
+    function get_unformatted_entry_body() {
+        return $this->entryBody;
+    }
+
+    /**
+     * get_formatted_entry_extended_body
+     * getter for ->entryExtendedBody
+     *
+     * @uses $CFG
+     * @return string Entry extended body/content run through moodle's format_text formatter and 
+     *                  with slashes stripped from database entry
+     */
+    function get_formatted_entry_extended_body() {
+        global $CFG;
+        include_once($CFG->libdir .'/weblib.php');
+        if ( isset($this->entryFormat) ) {
+            return format_text($this->entryExtendedBody, $this->entryFormat);
+        }
+        return stripslashes_safe($this->entryExtendedBody);
+    }
+
+    /**
+     * get_unformatted_entry_extended_body
+     * getter for ->entryExtendedBody
+     *
+     * @return string Entry extended body/content - raw string from database
+     */
+    function get_unformatted_entry_extended_body() {
+        return $this->entryExtendedBody;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param string $title New entry subject
+     */
+    function set_title($title) {
+        $this->entryTitle = $title;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param string $body New entry summary
+     */
+    function set_body($body) {
+        $this->entryBody = $body;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param string $extendedbody New entry content
+     */
+    function set_extendedbody($extendedbody) {
+        $this->entryExtendedBody = $extendedbody;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param string $format Moodle format_text format type.
+     */
+    function set_format($format) {
+        $this->entryFormat = $format;
+    }
+    
+    /**
+     *
+     * @return string
+     */
+    function get_entryurl() {
+        global $CFG;
+        return $CFG->wwwroot .'/blog/archive.php?userid='. $this->entryuserid .'&amp;postid='. $this->entryId;
+    }
+
+    /**
+     *  The url of the news feed containing this item. Uses global admin config to determin what feed type to point to.
+     * @return string
+     */
+    function get_entryfeedurl() {
+        global $CFG;
+        return $CFG->wwwroot .'/blog/rss.php?userid='. $this->entryuserid;
+    }
+
+    /**
+     *  
+     * @return string
+     */
+    function get_entryediturl() {
+        global $CFG;
+        return $CFG->wwwroot .'/blog/edit.php?userid='. $this->entryuserid .'&amp;editid='. $this->entryId;
+    }
+
+
+    /**
+     *  
+     * @return string
+     */
+    function get_entrydeleteurl() {
+        global $CFG;
+        return 'javascript:del(\''. $CFG->wwwroot .'/blog/\', '. $this->entryId .', '. $this->entryuserid .')';
+    }
+
+    /**
+     *  
+     * @return string
+     */
+    function get_entryblogthisurl() {
+        global $CFG;
+        return $CFG->wwwroot .'/blog/blogthis.php?userid='. $this->entryuserid .'&amp;act=use&amp;postid='. $this->entryId;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param int $userid The new author/owner's moodle user id
+     */
+    function set_userid($userid) {
+        $this->entryuserid = $userid;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param string $publishstate A new publish state for this entry. One of: 
+     *                  enum('draft','teacher','course','group','site','public') 
+     * @return bool True if new state is allowed (and applied), false if not.
+     */
+    function set_publishstate($publishstate) {
+        $applicablestates = array_keys(blog_applicable_publish_states($this->entryCourseId));
+        if (in_array($publishstate, $applicablestates)) {
+            $this->entryPublishState = $publishstate;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param int $courseid The course by id that this entry should be associated with.
+     */
+    function set_courseid($courseid) {
+        $this->entryCourseId = $courseid;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param int $groupid The groupid that this entry should be associated with.
+     */
+    function set_groupid($groupid) {
+        $this->entryGroupId = $groupid;
+    }
+
+    /**
+     * BlogEntry setters do not save to the database.
+     * To save changes call the BlogEntry->save() function when ready.
+     *
+     * @param array $catids An array of category ids to associate this entry with. 
+     */
+    function set_categoryids($catids) {
+        $this->entryCategoryIds = $catids;
+
+        if (!empty($this->entryCategoryIds)) {
+            if (!is_array($this->entryCategoryIds)) {
+                $this->entryCategoryIds = array($this->entryCategoryIds);
+            }
+            $this->entryCategoryIds = array_unique($this->entryCategoryIds);
+        }
+        
+        // now populate the entryCategories array
+        if (!empty($this->entryCategoryIds)) {
+            foreach ($this->entryCategoryIds as $categoryid) {
+                if (! $currcat = get_record('blog_categories', 'id', $categoryid)) {
+                    print 'Could not find category id '. $categoryid ."\n";
+                    $this->entryCategories[$categoryid] = '';
+                } else {
+                    $this->entryCategories[$categoryid] = $currcat->catname;
+                }
+            }
+        }
+    }
+
+    /**
+     * This function will determine if the user is logged in and
+     * able to make changes to the publish state of this entry
+     *
+     * @return bool True if user is allowed to change publish state
+     */
+    function user_can_change_publish_state() {
+        // figure out who the currently logged in user is.
+        // to change any publish state one must be logged in
+        global $USER;
+        if ( !isset($USER) || empty($USER) || !isset($USER->id) ) {
+            // only site members are allowed to edit entries
+            return 'Only site members are allowed to edit entries';
+        } else {
+            $uid = $USER->id;
+        }
+        if ( ($uid == $this->entryuserid) || (blog_is_blog_admin($this->entryuserid)) || (isadmin()) 
+             || (isset($this->entryCourseId) && isteacher($this->entryCourseId)) ) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * added by Daryl Hawes for moodle integration
+     *
+     * @param int $uid The user attempting to view this entry
+     * @return bool
+     */
+    function user_can_view($uid='') {
+        global $USER;
+
+        //first allow access to any post for admin users
+        if ( isadmin() ) {
+            return true;
+        }
+        
+        //get the logged in user's id if needed
+        if ($uid == '') {
+            if ( isset($USER) && isset($USER->id)) {
+                $uid = $USER->id;
+            }
+        }
+
+        if ($this->entryPublishState == 'public') {
+            return true;
+        } else if ($this->entryPublishState == 'draft') {
+            //only the owner is allowed to see their own draft message
+            if ($uid == $this->entryuserid) {
+                return true;
+            } 
+        } else if ($this->entryPublishState == 'site') {
+            //user has a valid member id and user is not a guest of the site
+            if ( ! $uid == '' && ! isguest() ) {
+                return true;
+            }
+        } else if ($this->entryPublishState == 'course') {
+            //there is a courseid and the user is a member of that course
+            if ( isset($this->entryCourseId) && (isteacher($this->entryCourseId, $uid) || isstudent($this->entryCourseId, $uid) ) ) {
+                return true;
+            }
+        } else if ($this->entryPublishState == 'teacher') {
+            if ( isset($this->entryCourseId) && isteacher($this->entryCourseId, $uid) )  {
+                return true;
+            }
+        } else if ($this->entryPublishState == 'group') {
+            if ( isset($this->entryGroupId) && ismember($this->entryGroupId, $uid) )  {
+                return true;
+            }            
+        }
+
+        //nothing qualified - the user requesting access is not allowed to view this entry!
+        return false;
+    }
+    
+    
+    /**
+     * @param bool $return If true a string value is returned. If this variable is set to false
+     *                      Then this function will print out the menu code and exit.
+     * @param bool $includehelp If true a help button linking to the batch_publish page
+     *                      will be included in the returned string
+     * @return string|nil  If the $return param is set to true a string is returned.
+     */
+    function get_publish_to_menu($return=true, $includehelp=true) {
+        $menu = '';
+        if ($this->user_can_change_publish_state() && blog_isediting() ) {
+            $menu .= '<div class="publishto">'. get_string('publishto', 'blog').': ';
+            $options = blog_applicable_publish_states($this->entryCourseId);
+            $menu .= choose_from_menu($options, $this->entryuserid .'-'. $this->entryId, $this->entryPublishState, '', '', '0', true);
+            $menu .= "\n".'</div>'."\n";
+            if ($includehelp) {
+                $menu .= helpbutton('batch_publish', get_string('batchpublish', 'blog'), 'blog', true, false, '', true);
+            }
+        }
+        if ($return) {
+            return $menu;
+        }
+        print $menu;
+    }
+        
+    /**
+     * Save this entry to the database. 
+     * This function can be used outside the BlogEntry class.
+     * ex:
+     * <code>
+     * $myBlogEntry = blogInfo->get_blog_entry_by_id(100);
+     * $myBlogEntry->set_title('New Title');
+     * $myBlogEntry->save();
+     * </code>
+     * This function will handle all of the security and data integrity checking for you
+     * @return null|string Error string returned when an error is encountered.
+     */
+    function save() {
+        //check if the user is authorized to make this change
+        // either they own this entry, are in the blog acl for this entry,
+        // are an admin user or they teach the course this entry is associated with
+        if ($this->user_can_change_publish_state()) {
+            $applicablestates = array_keys(blog_applicable_publish_states($this->entryCourseId));
+            if (in_array($this->entryPublishState, $applicablestates)) {
+                // Yes they are authorized so update the entry.
+                if ( $this->_update() ) {
+                    //print_object($this); //debug:
+
+                    //add a timestamp to the user preference for this userid to mark it updated
+                    set_user_preference('bloglastmodified', time(), $this->entryuserid);
+                    return;
+                } else {
+                    $error = 'An error occured saving this entry';
+                }
+            } else {
+                $error = 'Publish state '. $this->entryPublishState .' is not available for this user';
+            }
+        } else {
+            $error = 'User not allowed to edit this entry';
+        }
+        // must not have worked...
+        return $error;
+    }
+
+    /**
+     * _update
+     *
+     * This function is internal to the BlogEntry class and should not be used elsewhere.
+     * It takes the currently set member variables and writes them to the database.
+     * @return boolean
+     */
+    function _update() {
+    
+        global $db, $CFG;
+        // generate the modification date
+        $timenow = time();
+
+        //load up the data object with the latest data
+        $dataobject->id = intval($this->entryId);
+        $dataobject->summary = $this->entryBody;
+        $dataobject->content = $this->entryExtendedBody;
+        $dataobject->subject = $this->entryTitle;
+        $dataobject->format = intval($this->entryFormat);
+        $dataobject->userid = intval($this->entryuserid);
+        $dataobject->publishstate = $this->entryPublishState;
+
+        if ($this->entryCourseId) {
+            $dataobject->courseid = $this->entryCourseId;
+        } else {
+            $dataobject->courseid = SITEID;    //yu: in case change to all course
+        }
+        if ($this->entryGroupId) {
+            $dataobject->groupid = $this->entryGroupId;
+        } else {
+            $dataobject->groupid = 0;    //yu: in case we change to all groups
+        }
+        $dataobject->lastmodified = $timenow;
+
+        $dataobject->summary = ereg_replace("'", '<tick>', $dataobject->summary);
+        // The wysiwyg html editor adds a <br /> tag to the extendedbody.
+        // cleanup the extendedbody first
+        if ($dataobject->content == '<br />') {
+            $dataobject->content = '';
+        }
+        $dataobject->content= ereg_replace("'", '<tick>', $dataobject->content);
+        $dataobject->subject = ereg_replace("'", '<tick>', $dataobject->subject);
+        $dataobject->subject = addslashes($dataobject->subject);
+        $dataobject->summary = addslashes($dataobject->summary);
+        $dataobject->content = addslashes($dataobject->content);
+
+        // First update the entry's categories. Remove all, then add back those passed in
+        $sql = 'DELETE FROM '. $CFG->prefix .'blog_categories_entries WHERE entryid='. $this->entryId;
+        $rs = $db->Execute($sql);
+
+        if (!empty($this->entryCategoryIds)) {
+            if (!is_array($this->entryCategoryIds)) {
+                $this->entryCategoryIds = array($this->entryCategoryIds);
+            }
+            $this->entryCategoryIds = array_unique($this->entryCategoryIds);
+            foreach ($this->entryCategoryIds as $categoryid) {
+                $cat->entryid = $this->entryId;
+                $cat->categoryid = $categoryid;
+                insert_record('blog_categories_entries', $cat);
+            }
+        }
+
+        // next update the entry itself
+        if (update_record('post', $dataobject)) {
+            return true;
+        }
+        //failure
+        return false;
+    }
+
+}//end class BlogEntry
+?>
diff --git a/blog/class.BlogFilter.php b/blog/class.BlogFilter.php
new file mode 100755 (executable)
index 0000000..3920b85
--- /dev/null
@@ -0,0 +1,611 @@
+<?php // $Id$
+/*******************************************************************
+ * This class represents a set of active filters to be applied 
+ * in searching for or presenting blog entries.
+ * Retrieve  filtered entries by calling get_filtered_entries 
+ * rather than directly accessing the array as
+ * the function will fetch the entries for you if needed.
+ *
+ * @copyright 2003/2004/2005, Daryl Hawes ({@link http://www.cocoaobjects.com})
+ * @author Daryl Hawes
+ * @version  $Id$
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ * @package blog
+ ******************************************************************/
+
+include_once($CFG->dirroot.'/blog/lib.php');
+
+/*******************************************************************
+ * This class represents a set of active filters to be applied 
+ * in searching for or presenting blog entries. 
+ * Retrieve filtered entries by calling get_filtered_entries 
+ * rather than directly accessing the array as
+ * the function will fetch the entries for you if needed.
+ ******************************************************************/
+class BlogFilter {
+    // member variables
+    // you can use variable names directly to access properties.
+    //ie. $blogFilter->month
+    
+    var $startmonth; 
+    var $startday;
+    var $startyear;
+    var $endmonth; 
+    var $endday;
+    var $endyear;
+    var $tstart; //start time, calculated from the values of startmonth, startday and startyear
+    var $tend; //end time, calculated from the values of endmonth, endday and endyear
+    var $fetchlimit; //max # of entries to read from database
+    var $fetchstart; //entry # to start reading from database at
+    var $max_entries; //maximum number of matching entries available in database
+    var $sort; 
+    var $courseid;
+    var $userid; // moodle userid to specify a specific user's blog
+    var $postid; //id of a single blog entry
+    var $categoryid;
+    var $groupid;
+    var $blogtitle;
+    var $blogtagline;
+    var $blogtheme;
+    var $blogInfo;
+    var $memberlist; //do not access directly - use getter get_member_list()
+    var $filtered_entries = array();
+    var $baseurl;
+    var $filtertype;
+    var $filterselect;
+    var $tag;
+    var $keywords = NULL; //array of $keywordtype = $keywordstring
+
+    /**
+     * BlogFilter 
+     * class constructor that will build a new instance of a BlogFilter object
+     *
+     * @param  int $userid = the blog that the entries are to be found in. If 0 then all blogs are searched.
+     * @param  int $courseid = if needed the entries can be restricted to those associated with a given course.
+     * @param  int $postid = a specific blog entry that is being sought
+     */
+    function BlogFilter($userid='', $postid='', $courseid='', $groupid='', $fetchlimit='', $fetchstart='', $startmonth='', $startday='', $startyear='', $endmonth='', $endday='', $endyear='', $filtertype='', $filterselect='', $tagid='', $tag ='', $sort='lastmodified DESC') {
+
+        global $CFG;    //filter settings to be pass in for baseurl
+
+        if (!empty($userid) && $userid != 0 && $userid != '') {
+//            print "creating blogInfo object for user with id '$userid'<br />"; //debug
+            $this->blogInfo =& new BlogInfo($userid);
+        }
+        if ( empty($this->blogInfo) || empty($this->blogInfo->userid)) {
+            unset($this->blogInfo);
+            $this->blogtitle = '';
+            $this->blogtagline = '';
+            $this->blogtheme = '';
+        } else {
+
+            $this->blogtitle = &$this->blogInfo->blogtitle;
+            $this->blogtagline = &$this->blogInfo->blogtagline;
+            $this->blogtheme = $this->blogInfo->get_blog_theme();
+        }
+        
+        if (!is_numeric($courseid) || $courseid == 0 || $courseid == 1) {
+            $this->courseid = '';
+        } else {
+            $this->courseid = $courseid;        
+        }
+        if (! is_numeric($userid) ) {
+            $this->userid = 0;
+        } else {
+            $this->userid = $userid;
+        }
+        if (!is_numeric($fetchstart) ) {
+            $this->fetchstart = 0;
+        } else {
+            $this->fetchstart = $fetchstart;
+        }
+        if (!is_numeric($fetchlimit) ) {
+            $this->fetchlimit = $CFG->blog_default_fetch_num_entries;
+        } else {
+            $this->fetchlimit = $fetchlimit;
+        }
+        
+        $this->postid = $postid;
+        $this->groupid = $groupid;
+        
+        $this->startmonth = $startmonth;
+        $this->startday = $startday;
+        $this->startyear = $startyear;
+        $this->endmonth = $endmonth;
+        $this->endday = $endday;
+        $this->endyear = $endyear;
+
+        $this->tstart = blog_get_month_time($startyear, $startmonth, $startday);
+        $this->tend = blog_get_month_time($endyear, $endmonth, $endday);
+
+        $this->sort = $sort;
+        $this->filtertype = $filtertype;
+        $this->filterselect = $filterselect;
+        if ($tagid) {
+            $this->tag = $tagid;
+        } else if ($tag) {
+            $tagrec = get_record('tags', 'text', $tag);
+            $this->tag = $tagrec -> id;
+        }
+        // borrowed from Jon's table class
+        if(empty($this->baseurl)) {
+
+            $getcopy  = $_GET;
+            unset($getcopy['blogpage']);
+            
+            $strippedurl = strip_querystring(qualified_me());
+            if(!empty($getcopy)) {
+                $first = false;
+                $querystring = '';    
+                foreach($getcopy as $var => $val) {
+                    if(!$first) {
+                        $first = true;
+                        if ($var != 'filterselect' && $var != 'filtertype') {
+                            $querystring .= '?'.$var.'='.$val;
+                            $hasparam = true;
+                        } else {
+                            $querystring .= '?';
+                        }
+                    } else {
+                        if ($var != 'filterselect' && $var != 'filtertype') {
+                            $querystring .= '&amp;'.$var.'='.$val;
+                            $hasparam = true;
+                        }
+                    }
+                }
+                if (isset($hasparam)) {
+                    $querystring .= '&amp;';
+                } else {
+                    $querystring = '?';
+                }
+            } else {
+                $querystring = '?';
+            }
+            
+            $this->baseurl = strip_querystring(qualified_me()) . $querystring. 'filtertype='.$filtertype.'&amp;filterselect='.$filterselect.'&amp;';
+
+        }
+    }
+
+    /**
+     * borrowed from Jon's table class.
+     */
+    function define_baseurl($url) {
+        if(!strpos($url, '?')) {
+            $this->baseurl = $url.'?';
+        }
+        else {
+            $this->baseurl = $url.'&amp;';
+        }
+    }
+    
+    /**
+     *
+     */
+    function set_filtered_entries(&$blogentries) {
+        $this->filtered_entries = $blogentries;
+    }
+    
+    /**
+     * @return array Blog entries based on current filters
+     */
+    function get_filtered_entries() {
+        
+        if ( empty($this->filtered_entries) ) {
+            //no entries defined. try to fetch them.
+            $this->fetch_entries();
+        }
+        
+        if (!empty($this->filtered_entries)) {
+            //we have entries - return them
+            return $this->filtered_entries;
+        }
+        //still no entries - they must all be filtered away or there simply are none. return null.
+        return NULL;
+    }
+    
+    /**
+     *
+     */    
+    function get_bloginfo() {
+        //returns blog entries based on current filters as stored by but not created by this class
+        if (!empty($this->blogInfo) ) {
+            return $this->blogInfo;
+        }
+        return NULL;
+    }
+        
+    /**
+     * Using the member variables build a where clause and sql statment
+     * and fetch the correct blog entries from the database. The entries
+     * are then stored in the filtered_entries member variable.
+     *
+     * @uses $CFG
+     * @uses $USER
+     * @limit, if limit is false, then return all records
+     */
+    function fetch_entries($limit=true) {
+        global $CFG, $USER;
+/*
+        echo "<br />filter trying to do its job";
+        echo "<br />filtertype = $this->filtertype";
+        echo "<br />filterselect = $this->filterselect";
+        */
+        if (!isset($USER->id)) {
+            $USER->id = 0;    //hack, for guests
+        }
+        
+        if ($this->tag) {
+            $tagtablesql = $CFG->prefix.'blog_tag_instance bt, ';
+            $tagquerysql = ' AND bt.entryid = p.id AND bt.tagid = '.$this->tag.' ';
+        } else {
+            $tagtablesql = '';
+            $tagquerysql = '';
+        }
+        
+        /****************************************
+         * depending on the type, there are 4   *
+         * different possible sqls              *
+         ****************************************/
+        switch ($this->filtertype) {
+
+            case 'site':
+            
+                if (!isguest() && isloggedin()) {
+
+                    $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                            .$CFG->prefix.'user u
+                            WHERE p.userid = u.id '.$tagquerysql.'
+                            AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.')
+                            AND u.deleted = 0';
+
+                } else {
+
+                    $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                            .$CFG->prefix.'user u
+                            WHERE p.userid = u.id '.$tagquerysql.'
+                            AND p.publishstate = \'public\'
+                            AND u.deleted = 0';
+                }
+            
+            break;
+            
+            case 'course':
+                if ($this->filterselect != SITEID) {
+                    $SQL = '(SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                            .$CFG->prefix.'user_students u
+                            WHERE p.userid = u.userid '.$tagquerysql.'
+                            AND u.course = '.$this->filterselect.'
+                            AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.'))
+
+                            UNION
+
+                            (SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                            .$CFG->prefix.'user_teachers u
+                            WHERE p.userid = u.userid '.$tagquerysql.'
+                            AND u.course = '.$this->filterselect.'
+                            AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.'))';    //this will break for postgres, i think
+                } else {
+
+                    if (isloggedin()) {
+
+                        $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                                .$CFG->prefix.'user u
+                                WHERE p.userid = u.id '.$tagquerysql.'
+                                AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.')
+                                AND u.deleted = 0';
+
+                    } else {
+
+                        $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                                .$CFG->prefix.'user u
+                                WHERE p.userid = u.id '.$tagquerysql.'
+                                AND p.publishstate = \'public\'
+                                AND u.deleted = 0';
+                    }
+
+                }
+            
+            break;
+            
+            case 'group':
+
+                $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                        .$CFG->prefix.'groups_members m
+                        WHERE p.userid = m.userid '.$tagquerysql.'
+                        AND m.groupid = '.$this->filterselect.'
+                        AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.')';
+            
+            break;
+            
+            case 'user':
+
+                $SQL = 'SELECT p.* FROM '.$CFG->prefix.'post p, '.$tagtablesql
+                        .$CFG->prefix.'user u
+                        WHERE p.userid = u.id '.$tagquerysql.'
+                        AND u.id = '.$this->filterselect.'
+                        AND (p.publishstate = \'site\' OR p.publishstate = \'public\' OR p.userid = '.$USER->id.')';
+            
+            break;
+
+
+        }
+        
+        if ($this->fetchstart !== '' && $limit) {    //this can be changed to use mysql_paging_limit
+            $limit = sql_paging_limit($this->fetchstart, $this->fetchlimit);
+        } else {
+            $limit = '';
+        }
+        
+        $orderby = ' ORDER BY '. $this->sort .' ';
+
+        //echo 'Debug: BlogFilter fetch_entries() sql="'. $SQL . $orderby . $limit .'"<br />'. $this->categoryid; //debug
+
+        $records = get_records_sql($SQL . $orderby . $limit);
+
+//        print_object($records); //debug
+       
+        if (empty($records)) {
+            return array();
+        } else {
+            $blogEntries = array();
+            foreach($records as $record) {
+                $blogEntry = new BlogEntry($record);
+                $blogEntries[] = $blogEntry;
+            }
+        }
+
+//        echo 'Debug: blog entries retrieved in fetch_entries function  of BlogFilter class:<br />'; //debug
+//        print_object($blogEntries); //debug
+
+        $this->filtered_entries = $blogEntries;
+
+        return $this->filtered_entries;
+    }
+
+    /**
+     * get_where_clause
+     *
+     * This function will take all of this BlogFilter's instance variables and 
+     * calculate the proper sql string needed to fetch the appropriate entries.
+     * fields are referred to as e.* in the post table
+     * and c.* if they are in the blog_categories_entries table
+     *
+     * @return string The where clause for searching using this filter's settings
+     */
+    function get_where_clause() {
+        $hascats = false;
+        $where = '';
+        if ( !empty($this->categoryid) ) {
+            $hascats = true;
+        }
+
+        if ($this->is_userid_valid()) {
+            $where .= 'e.userid='. $this->userid;
+        }
+        if ($this->is_courseid_valid()) {
+            if ($where != '') {
+                $where .= ' AND ';
+            }
+            $where .= 'e.courseid='. $this->courseid;
+        }
+
+        if ($this->is_userid_valid() && $this->is_postid_valid()) {
+            // a blog and a specific entry in that blog were specified. our mission is clear
+            if ($where != '') {
+                $where .= ' AND ';
+            }
+            $where .= 'e.id='. $this->postid .' ';
+        } else {
+            // we do not have a specific post id, so get all posts that match additional criteria if present
+            if ($hascats) {
+                if ($where != '') {
+                    $where .= ' AND ';
+                }
+                // where the discovered post id matches the categories_entries table entryid and the catid matches
+                $where .= ' e.id = c.entryid AND c.categoryid='. $this->categoryid;
+                
+            }
+        }
+        
+        if ( !empty($this->tstart) && !empty($this->tend) ) {
+            if ($where != '') {
+                $where .= ' AND ';
+            }
+            $where .= 'e.lastmodified >= '. $this->tstart .' AND e.lastmodified <= '. $this->tend;
+        }
+
+        //http://www.techonthenet.com/sql/like.htm
+        $keywords = $this->keywords;
+        if (!empty($keywords) && count($keywords) > 0) {
+            if (!empty($keywords['body'])) {
+                if ($where != '') {
+                    $where .= ' AND ';
+                }
+                $where .= "(e.body LIKE '%". $keywords['body'] ."%' OR e.extendedbody LIKE '%". $keywords['body'] ."%') ";
+            }
+            if (!empty($keywords['body'])) {
+                if ($where != '') {
+                    $where .= ' AND ';
+                }
+                $where .= "(e.title LIKE '%". $keywords['title'] ."%') ";
+            }
+        }
+        return $where;
+    }
+    
+    /**
+     * get the count of entries in db without applying full filtering
+     */
+    function get_entry_count($where='', $hascats=false) {
+        global $CFG;
+        $sql = 'post e';
+        if ($hascats) {
+            $sql .= ', '. $CFG->prefix .'blog_categories_entries c ';
+        }
+        if (empty($where)) {
+            $where = $this->get_where_clause();
+        }
+        return count_records_select($sql, $where);
+    }
+
+    /**
+     * get the count of viewable entries, easiest way is to count fetch_entries
+     * this is used for print_paging_bar
+     */
+    function get_viewable_entry_count($where='', $hascats=false) {
+        $blogEntries = $this->fetch_entries(false);
+        return count($blogEntries);
+    }
+
+    /**
+     * get count of entries as they have been fetched from the fully filtered query
+     */
+    function get_filtered_entry_count() {
+        global $CFG;
+
+        $entries = $this->get_filtered_entries();
+        return count($entries);
+    }
+        
+    /**
+     * Use this function to retrieve a link to a blog page (typically not the one 
+     * you are currently processing) which contains the correct blog filter information
+     * to maintain the user's filtered view when progressing from page to page.
+     *
+     * The unused param is defined as either
+     * <code>
+     * $unused = array('userid', 'courseid', 'groupid');
+     * </code>
+     * or
+     * <code>
+     * $unused = 'startyear';
+     * </code>
+     * @param string $baseurl The url to be added to the full href before the getvars
+     * @param array|string $unused Can be an array of ivar names or a single variable name
+     * @return string A link to the specified baseurl along with the correct getvars for this filter.
+     */
+    function get_complete_link($baseurl, $linktext, $unused='') {
+        $getargs = $this->get_getvars($unused);
+        $link = '<a href="'. $baseurl;
+        $link .= $getargs . '">';
+        $link .= $linktext . '</a>';
+        return $link;
+    }
+    
+    /**
+    * The unused param is defined as either
+    * <code>
+    * $unused = array('userid', 'courseid', 'groupid');
+    * </code>
+    * or
+    * <code>
+    * $unused = 'startyear';
+    * </code>
+    * @param array|string $unused Can be an array of ivar names or a single variable name
+    */
+    function get_getvars($unused) {
+        $getargs = '?';
+        if(!is_array($unused)) {
+            $unused = array($unused);
+        }
+        if (!is_array($unused)) {
+            //argument is not an array, hopefully it's a string. wrap it in an array for comparisons below.
+            $unused = array($unused);
+        }
+        if (!in_array('startmonth', $unused)) {
+            $getargs .= '&amp;m=' . $this->startmonth; 
+        }
+        if (!in_array('startday', $unused)) {
+            $getargs .= '&amp;d=' . $this->startday;
+        }
+        if (!in_array('startyear', $unused)) {
+            $getargs .= '&amp;y=' . $this->startyear;
+        }
+        if (!in_array('limit', $unused)) {
+            $getargs .= '&amp;limit=' . $this->fetchlimit;
+        }
+        if (!in_array('formstart', $unused)) {
+            $getargs .= '&amp;formstart=' . $this->fetchstart;
+        }
+        if (!in_array('courseid', $unused)) {
+            $getargs .= '&amp;courseid=' . $this->courseid;
+        }
+        if (!in_array('userid', $unused)) {
+            $getargs .= '&amp;userid=' . $this->userid;
+        }
+        if (!in_array('categoryid', $unused)) {
+            $getargs .= '&amp;categoryid=' . $this->categoryid;
+        }
+        if (!in_array('groupid', $unused)) {
+            $getargs .= '&amp;groupid=' . $this->groupid;
+        }
+        return $getargs;
+    }
+    
+    function is_userid_valid() {
+        if (is_numeric($this->userid) && $this->userid != 0 && $this->userid != '') {
+            return true;
+        } 
+        return false;
+    }
+
+    function is_groupid_valid() {
+        if (is_numeric($this->groupid) && $this->groupid != 0 && $this->groupid != '') {
+            return true;
+        } 
+        return false;
+    }
+
+    function is_courseid_valid() {
+        if (is_numeric($this->courseid) && $this->courseid != 0 && $this->courseid != '') {
+            return true;
+        } 
+        return false;
+    }
+
+    function is_postid_valid() {
+        if (is_numeric($this->postid) && $this->postid != 0 && $this->postid != '') {
+            return true;
+        } 
+        return false;
+    }
+
+    /**
+     * get_member_list
+     *
+     * @param string $sort
+     * @param int $limitnum
+     * @return object containing records with ->id and ->title of member blogs
+     */
+    function get_member_list($sort='', $limitnum='') {
+        global $CFG;
+        if (!empty($this->memberlist)) {
+            return $this->memberlist;
+        }
+
+        //temporarily change $this->fetchstart to unlimit the entries from db and fetch
+        $oldstart = $this->fetchstart;
+        $this->fetchstart = '';
+        $entries = $this->fetch_entries();
+        $this->fetchstart = $oldstart;
+
+        $records = array();
+        $bids = array();
+        foreach ($entries as $entry) {
+            if (!in_array($entry->entryuserid, $bids)) {
+                $bids[$entry->entryuserid] = $entry->entryuserid;
+            }
+        }
+
+        foreach ($bids as $bid) {
+            $thisrecord->id = $bid;
+            $thisrecord->title = get_user_preferences('blogtitle', $CFG->blog_default_title, $bid);
+            $records[] = $thisrecord;
+        }
+        $this->memberlist = $records;
+        return $records;
+    }
+
+}    //end class BlogFilter
+?>
diff --git a/blog/class.BlogInfo.php b/blog/class.BlogInfo.php
new file mode 100755 (executable)
index 0000000..11332a4
--- /dev/null
@@ -0,0 +1,473 @@
+<?php //$Id$
+    /**
+     * class.BlogInfo.php
+     * Author: Jason Buberel
+     * Copyright (C) 2003, Jason Buberel
+     * jason@buberel.org
+     * http://www.buberel.org/
+     *
+     *******************************************************************
+     * This program is free software; you can redistribute it and/or modify it
+     * under the terms of the GNU General Public License as published by the
+     * Free Software Foundation; either version 2 of the License, or (at your
+     * option) any later version.
+     * 
+     * This program is distributed in the hope that it will be useful, but
+     * WITHOUT ANY WARRANTY; without even the implied warranty of
+     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     * General Public License for more details.
+     * 
+     * You should have received a copy of the GNU General Public License along
+     * with this program; if not, write to the Free Software Foundation, Inc.,
+     * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+     *******************************************************************
+     *
+     * This class is used to represent a single weblog. It gives the
+     * developer access to all of the normal properties of the weblog.
+     * Through the use of the required BlogEntry class, you should be able
+     * to access all the data related to this particular weblog.
+     *
+     * To use BlogInfo, you create a new instance using the provided
+     * constructor:
+     *  include_once("class.BlogInfo.php");
+     *  $userid = 2;
+     *  $myBlog = new BlogInfo($userid);
+     *
+     * Once instantiated, the BlogInfo instance can be used to obtain
+     * information about the blog:
+     *
+     *  $myTitle = $myBlog->get_blog_title();
+     *
+     * The three most useful methods are those used to retrieve BlogEntries:
+     *
+     *  $someBlogEntry = $myBlog->get_blog_entry_by_id(200); // fetch the 200th blog entry.
+     *  $blogEntryList = $myBlog->get_last_N_entries(10); // fetch the 10 most recent
+     *                                                 // blog entries.
+     *  foreach ($blogEntryList as $blogEntry) {
+     *      print "Blog Entry Title: ($blogEntry->entryId) $blogEntry->get_blog_title()<br/>";
+     *  }
+     */
+
+global $CFG;
+include_once($CFG->dirroot.'/blog/lib.php');
+include_once($CFG->dirroot.'/blog/class.BlogEntry.php');
+
+class BlogInfo {
+    // member variables
+    var $userid; // moodle userid
+    var $blogtitle; // user preference blog_title
+    var $blogtagline; // user preference blog_tagline
+    var $blogtheme; // user preference blog_theme - id of the template being used for this blog.
+    
+    // lazy loading member variables
+    // DO NOT directly refer to these member vars. Instead use their
+    // getter functions to ensure they are loaded properly
+    var $blogadminuser = NULL; // moodle user object for this userid
+    var $blogadminname = NULL; // userid -> blog_users.name
+    var $blogadminemail = NULL; // userid -> blog_users.email
+    var $blogadminurl = NULL; // userid -> blog_users.url
+    var $blogEntries = NULL; // an array of entries for this blog. empty by default.
+
+    /**
+     * constructor- used to create the BlogInfo instance
+     * and populate it with information about the blog.
+     */
+    function BlogInfo($userid) {
+        global $CFG;
+
+        if ($userid == 0 || empty($userid)) {
+            return NULL;
+        }
+        
+        $this->blogEntries = array();
+        $this->userid = $userid;
+        $this->blogtitle = stripslashes_safe(get_user_preferences('blogtitle', $CFG->blog_default_title, $userid));
+        $this->blogtagline = stripslashes_safe(get_user_preferences('blogtagline', '', $userid));
+        $this->bloguseextendedbody = get_user_preferences('bloguseextendedbody', false, $userid);
+        $this->blogtheme = get_user_preferences('blogtheme', 0, $userid); //Daryl Hawes note: investigate blogtheme usage again
+/*        if ($this->userid == 0) { 
+            $this->userid = 1;
+        }*/
+    }
+
+////////// getters and setters ///////////////
+
+    /**
+     * Getter for this blog's title, ->blogtitle
+     */
+    function get_blog_title() {
+        if (!isset($this->blogtitle)) {
+            return '';
+        }
+        return $this->blogtitle;
+    }
+
+    /**
+     *
+     */
+    function set_blog_title($title) {
+        if (set_user_preference('blogtitle', $title, $this->userid)) {
+            $this->blogtitle = $title;
+            return true;
+        }
+        return false;
+    }
+   
+    /**
+     *
+     */
+    function get_blog_use_extended_body() {
+        return $this->bloguseextendedbody;
+    }
+
+    /**
+     *
+     */
+    function set_blog_use_extended_body($extend) {
+        if (set_user_preference('bloguseextendedbody', $extend, $this->userid)) {
+            $this->useextendedbody = $extend;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     *
+     */
+    function get_blog_tagline() {
+        if (!isset($this->blogtagline)) {
+            return '';
+        }
+        return $this->blogtagline;
+    }
+    
+    /**
+     *
+     */
+    function set_blog_tagline($tag) {
+        if (set_user_preference('blogtagline', $tag, $this->userid)) {
+            $this->blogtagline = $tag;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 
+     */
+    function get_blog_url() {
+        global $CFG;
+        return $CFG->blog_blogurl .'?userid='. $this->userid;
+    }
+    // no setter for blog URL, as there is no place to put it.
+
+    /**
+     *
+     */
+    function get_blog_theme() {
+        return $this->blogtheme;
+    }
+    
+    /**
+     *
+     */
+    function set_blog_theme($theme) {
+        if (set_user_preference('blogtheme', $theme, $this->userid)) {
+            $this->blogtheme = $theme;
+            return true;
+        }
+        return false;
+    }
+    
+    /**
+     * Getter for this blog owner's full name
+     * Warning: Do not attempt to directly access ->blogadminname from this class
+     *          as it is loaded lazily - NULL until this getter is called
+     */
+    function get_blogadminname() {
+        if (empty($this->blogadminuser)) {
+            $this->blogadminuser = get_user_info_from_db('id', $this->userid);
+            $this->blogadminname = fullname($this->blogadminuser);
+        }
+        return $this->blogadminname;
+    }
+
+    /**
+     * Getter for this blog owner's email address
+     * Warning: Do not attempt to directly access ->blogadminemail from this class
+     *          as it is loaded lazily - NULL until this getter is called
+     */
+    function get_blogadminemail() {
+        if (empty($this->blogadminuser)) {
+            $this->blogadminuser = get_user_info_from_db('id', $this->userid);
+            $this->blogadminemail = $this->blogadminuser->email;
+        }
+        return $this->blogadminemail;
+    }
+
+    /**
+     * Getter for this blog owner's contact URL
+     * Warning: Do not attempt to directly access ->blogadminurl from this class
+     *          as it is loaded lazily - NULL until this getter is called
+     */
+    function get_blogadminurl() {
+        if (empty($this->blogadminuser)) {
+            $this->blogadminuser = get_user_info_from_db('id', $this->userid);
+            $this->blogadminurl = $this->blogadminuser->url;
+        }
+        return $this->blogadminurl;
+    }
+
+    function get_blogurl() {
+        return NULL;
+    }
+
+    /**
+     * return the timestamp when this blog was last updated.
+     * @return string Date of last modification in moodle's userdate() format.
+     */
+    function get_last_update() {
+        $where = 'userid='. $this->userid;
+        $record = get_record_select('post', $where, 'lastmodified');
+        return userdate($record->lastmodified);
+    }
+
+    /**
+     * Use this function to get a single numbered BlogEntry object
+     * for this blog.
+     * @todo perhaps a member array could be used to store these fetched BlogEntry objects
+     *      in case the same entry is requested from this same bloginfo object later
+     */
+    function get_blog_entry_by_id($entryId) {
+        global $CFG;
+    
+        foreach ($this->blogEntries as $cachedentry) {
+            if ($cachedentry->entryId == $entryId) {
+                return $cachedentry;
+            }
+        }
+        $record = get_record('post', 'id', $entryId);    //teachers should be able to edit people's blogs, right?
+        //$record = get_record('post', 'author', $this->userid, 'id', $entryId);
+        // may have zero entries. in that case, return null.
+        if (empty($record)) {
+            // the result set is empty. return null.
+            return NULL;
+        } else {
+            // create the new blog entry object...
+            $blogEntry = new BlogEntry($record);
+            //cache the blogEntry in member var for future use if needed
+            $this->blogEntries[] = $blogEntry;
+            $this->blogEntries = array_unique($this->blogEntries);
+        }
+        return $blogEntry;
+    }
+
+    /**
+     * This function will remove the specified blog entry. It will
+     * perform any of the security checks necessary to ensure that the
+     * user is authorized to remove the entry. It will return false
+     * if there was an error trying to delete the entry.
+     * @param int $entryID The blog entry to delete by id
+     */
+    function delete_blog_entry_by_id($entryId) {
+        // figure out who the currently logged in user is.
+        global $USER;
+        if ( !isset($USER) || empty($USER) || !isset($USER->id) ) {
+            return false;
+        }
+        $uid = $USER->id;
+        // retrieve the entry.
+        $blogEntry = $this->get_blog_entry_by_id($entryId);
+        
+        if (empty($blogEntry) ) {
+            return false;
+        }
+
+        if (($uid == $blogEntry->entryuserid) || (blog_is_blog_admin($this->userid)) || (isadmin())) {
+            // yes, they are authorized, so remove the entry.
+            if ( $blogEntry->delete() ) {
+                unset($this->blogEntries[$blogEntry]);
+                return true;
+            }
+        }
+        // must not have worked...
+        return false;
+    }
+    
+    /**
+     * This function will return a list, in order, formatted as "YYYY-MM" for each year and month
+     * in which this blog has entries. Used by the 'blog_archives' block.
+     */
+    function get_year_month_of_entries() {
+        $entries = get_records('post', 'userid', $this->userid, 'lastmodified ASC');
+        $dateHash = array();
+        if ( !empty($entries) ) {
+            foreach ($entries as $entry) {
+                $date = $entry->lastmodified;
+                $truncDate = date("Y-m", $date); // this will return 9999-99 for the date.
+                $dateHash[$truncDate] = 1;
+            }
+        }
+        return $dateHash;
+    }
+
+    /**
+     * Use this method to insert/create a new entry in the post table for
+     * this blog. The entry id of the new blog entry will be returned if the
+     * insertion is successful.
+     * @param string $title .
+     * @param string $body .
+     * @param string $extendedbody .
+     * @param int $userid .
+     * @param int $formatId .
+     * @param string $publishstate 'draft', 'teacher', 'course', 'group', 'site', 'public'
+     * @param int $courseid .
+     * @param int $groupid .
+     * @return int
+     */
+    function insert_blog_entry($title, $body, $extendedbody, $userid, $formatId, $publishstate='draft', $courseid='', $groupid='') {
+        global $CFG;
+        
+        // first, make sure the title and body are safe for insert.
+        $title = ereg_replace("'", '<tick>', $title);
+        $body = ereg_replace("'", '<tick>', $body);
+        // The wysiwyg html editor adds a <br /> tag to the extendedbody.
+        // cleanup the extendedbody first
+        if ($extendedbody == '<br />' || $extendedbody == '<br /><br />') {
+            $extendedbody = '';
+        }
+        $extendedbody = ereg_replace("'", '<tick>', $extendedbody);
+        $title = addslashes($title);
+        $body = addslashes($body);
+        $extendedbody = addslashes($extendedbody);
+
+        // come up with a new timestamp to insert.
+        // now insert the new entry.
+        $dataobject->summary = $body;
+        $dataobject->content = $extendedbody;
+        $dataobject->userid = $userid;
+        $dataobject->subject = $title;
+        $dataobject->format = $formatId;
+        
+        $timenow = time();
+        $dataobject->lastmodified = $timenow;        
+        $dataobject->created = $timenow;
+        $dataobject->publishstate = $publishstate;
+        if ($courseid != '') {
+            $dataobject->courseid = intval($courseid);
+        }
+        if ($groupid != '') {
+            $dataobject->groupid = intval($groupid);
+        }
+
+        $newentryid = insert_record('post', $dataobject);
+
+        if ($newentryid) {
+            // entry was created and $newentryid is its entryid.
+            
+            // create a unique hash for this id that will be its alternate identifier
+            unset($dataobject);
+            $dataobject->id = $newentryid;
+            $dataobject->uniquehash = md5($userid.$CFG->wwwroot.$newentryid);
+            update_record('post', $dataobject);
+            
+            // now create category entries
+            if (!empty($categoryids)) {
+                foreach ($categoryids as $categoryid) {
+                    $cat->entryid = $newentryid;
+                    $cat->categoryid = $categoryid;
+                    insert_record('blog_categories_entries', $cat);
+                }
+            }
+            // insert lastmodified into user pref so that recently modified blogs can be identified easily without joining tables
+            set_user_preference('bloglastmodified', $timenow, $this->userid);
+            return $newentryid;
+        }
+        return null;
+    }
+
+    /**
+     * Discovers the number of entries for this blog
+     * @return int Entry count
+     */
+    function get_entry_count() {
+        return count_records('post', 'userid', $this->userid);
+    }
+
+    /**
+        * returns the N most recent BlogEntry objects
+     * for this blog
+     */
+    function get_last_N_entries($n) {
+        return $this->get_blog_entries_by_range($n, 0);
+    }
+    
+    /**
+     *
+     */
+    function get_blog_entries_by_range($limit, $start) {
+        global $USER;
+    
+        $sqlsnippet = 'userid='. $this->userid;
+        $sort = 'id DESC';
+        $records = get_records_select('post', $sqlsnippet, $sort, '*', $start, $limit);
+        if (empty($records)) {
+            return array();
+        } else {
+            $blogEntries = array();
+            foreach($records as $record) {
+                $blogEntry = new BlogEntry($record);
+                //ensure that the user has rights to view this entry
+                if ($blogEntry->user_can_view() ) {
+                    $blogEntries[] = $blogEntry;
+                }
+            }
+//            print_object($blogEntries); //debug
+            //cache the blogEntries in member var for future use if needed
+            $this->blogEntries = array_merge($this->blogEntries, $blogEntries);
+            $this->blogEntries = array_unique($this->blogEntries);
+
+            return $blogEntries;
+        }
+    }
+
+        /**
+        * update_blog_entry_by_id
+        *
+        * this funciton will update the selected blog entry after performing
+        * security checks to make sure the user is authorized to perform the update.
+        * Used by api.php
+        * @uses USER
+        */
+    function update_blog_entry_by_id($entryId, $title, $body, $extendedbody, $formatId, $categoryId, $publishstate='draft', $courseid='', $groupid='') {
+        // figure out who the currently logged in user is.
+        global $USER;
+
+        if ( !isset($USER) || empty($USER) || !isset($USER->id) ) {
+            return false;
+        } else {
+            $uid = $USER->id;
+        }
+        $body = ereg_replace("'", '<tick>', $body);
+        $extendedbody = ereg_replace("'", '<tick>', $extendedbody);
+        $title = ereg_replace("'", '<tick>', $title);
+        $title = addslashes($title);
+        $body = addslashes($body);
+        $extendedbody = addslashes($extendedbody);
+
+        // retrieve the entry
+        $blogEntry = $this->get_blog_entry_by_id($entryId);
+        //check if the user is authorized to make this change
+        if ( ($uid == $blogEntry->entryUserId) || (blog_is_blog_admin($this->userid)) || (isadmin()) ) {
+            // Yes they are authorized so update the entry.
+            if ( $blogEntry->update($title, $body, $extendedbody, $formatId, $categoryId, $publishstate, $courseid, $groupid) ) {
+                return true;
+            }
+        }
+        // must not have worked...
+        return false;
+        
+    }
+    
+}
+?>
diff --git a/blog/class.HttpClient.php b/blog/class.HttpClient.php
new file mode 100644 (file)
index 0000000..d04b1b5
--- /dev/null
@@ -0,0 +1,338 @@
+<?php
+
+/* Version 0.9, 6th April 2003 - Simon Willison ( http://simon.incutio.com/ )
+   Manual: http://scripts.incutio.com/httpclient/
+*/
+
+class HttpClient {
+    // Request vars
+    var $host;
+    var $port;
+    var $path;
+    var $method;
+    var $postdata = '';
+    var $cookies = array();
+    var $referer;
+    var $accept = 'text/xml,application/xml,application/xhtml+xml,text/html,text/plain,image/png,image/jpeg,image/gif,*/*';
+    var $accept_encoding = 'gzip';
+    var $accept_language = 'en-us';
+    var $user_agent = 'Incutio HttpClient v0.9';
+    // Options
+    var $timeout = 20;
+    var $use_gzip = true;
+    var $persist_cookies = true;  // If true, received cookies are placed in the $this->cookies array ready for the next request
+                                  // Note: This currently ignores the cookie path (and time) completely. Time is not important, 
+                                  //       but path could possibly lead to security problems.
+    var $persist_referers = true; // For each request, sends path of last request as referer
+    var $debug = false;
+    var $handle_redirects = true; // Auaomtically redirect if Location or URI header is found
+    var $max_redirects = 5;
+    var $headers_only = false;    // If true, stops receiving once headers have been read.
+    // Basic authorization variables
+    var $username;
+    var $password;
+    // Response vars
+    var $status;
+    var $headers = array();
+    var $content = '';
+    var $errormsg;
+    // Tracker variables
+    var $redirect_count = 0;
+    var $cookie_host = '';
+    function HttpClient($host, $port=80) {
+        $this->host = $host;
+        $this->port = $port;
+    }
+    function get($path, $data = false) {
+        $this->path = $path;
+        $this->method = 'GET';
+        if ($data) {
+            $this->path .= '?'. $this->buildQueryString($data);
+        }
+        return $this->doRequest();
+    }
+    function post($path, $data) {
+        $this->path = $path;
+        $this->method = 'POST';
+        $this->postdata = $this->buildQueryString($data);
+       return $this->doRequest();
+    }
+    function buildQueryString($data) {
+        $querystring = '';
+        if (is_array($data)) {
+            // Change data in to postable data
+            foreach ($data as $key => $val) {
+                if (is_array($val)) {
+                    foreach ($val as $val2) {
+                        $querystring .= urlencode($key) .'='. urlencode($val2) .'&';
+                    }
+                } else {
+                    $querystring .= urlencode($key) .'='. urlencode($val) .'&';
+                }
+            }
+            $querystring = substr($querystring, 0, -1); // Eliminate unnecessary &
+        } else {
+            $querystring = $data;
+       }
+       return $querystring;
+    }
+    function doRequest() {
+        // Performs the actual HTTP request, returning true or false depending on outcome
+        if (!$fp = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout)) {
+            // Set error message
+            switch($errno) {
+                case -3:
+                    $this->errormsg = 'Socket creation failed (-3)';
+                case -4:
+                    $this->errormsg = 'DNS lookup failure (-4)';
+                case -5:
+                    $this->errormsg = 'Connection refused or timed out (-5)';
+                default:
+                    $this->errormsg = 'Connection failed ('. $errno .')';
+                $this->errormsg .= ' '. $errstr;
+                $this->debug($this->errormsg);
+            }
+            return false;
+        }
+        socket_set_timeout($fp, $this->timeout);
+        $request = $this->buildRequest();
+        $this->debug('Request', $request);
+        fwrite($fp, $request);
+       // Reset all the variables that should not persist between requests
+       $this->headers = array();
+       $this->content = '';
+       $this->errormsg = '';
+       // Set a couple of flags
+       $inHeaders = true;
+       $atStart = true;
+       // Now start reading back the response
+       while (!feof($fp)) {
+            $line = fgets($fp, 4096);
+            if ($atStart) {
+                // Deal with first line of returned data
+                $atStart = false;
+                if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/', $line, $m)) {
+                    $this->errormsg = 'Status code line invalid: '. htmlentities($line);
+                    $this->debug($this->errormsg);
+                    return false;
+                }
+                $http_version = $m[1]; // not used
+                $this->status = $m[2];
+                $status_string = $m[3]; // not used
+                $this->debug(trim($line));
+                continue;
+            }
+            if ($inHeaders) {
+                if (trim($line) == '') {
+                    $inHeaders = false;
+                    $this->debug('Received Headers', $this->headers);
+                    if ($this->headers_only) {
+                        break; // Skip the rest of the input
+                    }
+                    continue;
+                }
+                if (!preg_match('/([^:]+):\\s*(.*)/', $line, $m)) {
+                    // Skip to the next header
+                    continue;
+                }
+                $key = strtolower(trim($m[1]));
+                $val = trim($m[2]);
+                // Deal with the possibility of multiple headers of same name
+                if (isset($this->headers[$key])) {
+                    if (is_array($this->headers[$key])) {
+                        $this->headers[$key][] = $val;
+                    } else {
+                        $this->headers[$key] = array($this->headers[$key], $val);
+                    }
+                } else {
+                    $this->headers[$key] = $val;
+                }
+                continue;
+            }
+            // We're not in the headers, so append the line to the contents
+            $this->content .= $line;
+        }
+        fclose($fp);
+        // If data is compressed, uncompress it
+        if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] == 'gzip') {
+            $this->debug('Content is gzip encoded, unzipping it');
+            $this->content = substr($this->content, 10); // See http://www.php.net/manual/en/function.gzencode.php
+            $this->content = gzinflate($this->content);
+        }
+        // If $persist_cookies, deal with any cookies
+        if ($this->persist_cookies && isset($this->headers['set-cookie']) && $this->host == $this->cookie_host) {
+            $cookies = $this->headers['set-cookie'];
+            if (!is_array($cookies)) {
+                $cookies = array($cookies);
+            }
+            foreach ($cookies as $cookie) {
+                if (preg_match('/([^=]+)=([^;]+);/', $cookie, $m)) {
+                    $this->cookies[$m[1]] = $m[2];
+                }
+            }
+            // Record domain of cookies for security reasons
+            $this->cookie_host = $this->host;
+        }
+        // If $persist_referers, set the referer ready for the next request
+        if ($this->persist_referers) {
+            $this->debug('Persisting referer: '. $this->getRequestURL());
+            $this->referer = $this->getRequestURL();
+        }
+        // Finally, if handle_redirects and a redirect is sent, do that
+        if ($this->handle_redirects) {
+            if (++$this->redirect_count >= $this->max_redirects) {
+                $this->errormsg = 'Number of redirects exceeded maximum ('. $this->max_redirects .')';
+                $this->debug($this->errormsg);
+                $this->redirect_count = 0;
+                return false;
+            }
+            $location = isset($this->headers['location']) ? $this->headers['location'] : '';
+            $uri = isset($this->headers['uri']) ? $this->headers['uri'] : '';
+            if ($location || $uri) {
+                $url = parse_url($location.$uri);
+                // This will FAIL if redirect is to a different site
+                return $this->get($url['path']);
+            }
+        }
+        return true;
+    }
+    function buildRequest() {
+        $headers = array();
+        $headers[] = "{$this->method} {$this->path} HTTP/1.0"; // Using 1.1 leads to all manner of problems, such as "chunked" encoding
+        $headers[] = 'Host: '. $this->host;
+        $headers[] = 'User-Agent: '. $this->user_agent;
+        $headers[] = 'Accept: '. $this->accept;
+        if ($this->use_gzip) {
+            $headers[] = 'Accept-encoding: '. $this->accept_encoding;
+        }
+        $headers[] = 'Accept-language: '. $this->accept_language;
+        if ($this->referer) {
+            $headers[] = 'Referer: '. $this->referer;
+        }
+        // Cookies
+        if ($this->cookies) {
+            $cookie = 'Cookie: ';
+            foreach ($this->cookies as $key => $value) {
+                $cookie .= "$key=$value; ";
+            }
+            $headers[] = $cookie;
+        }
+        // Basic authentication
+        if ($this->username && $this->password) {
+            $headers[] = 'Authorization: BASIC '. base64_encode($this->username .':'. $this->password);
+        }
+        // If this is a POST, set the content type and length
+        if ($this->postdata) {
+            $headers[] = 'Content-Type: application/x-www-form-urlencoded';
+            $headers[] = 'Content-Length: '. strlen($this->postdata);
+       }
+       $request = implode("\r\n", $headers) ."\r\n\r\n". $this->postdata;
+       return $request;
+    }
+    function getStatus() {
+        return $this->status;
+    }
+    function getContent() {
+        return $this->content;
+    }
+    function getHeaders() {
+        return $this->headers;
+    }
+    function getHeader($header) {
+        $header = strtolower($header);
+        if (isset($this->headers[$header])) {
+            return $this->headers[$header];
+        } else {
+            return false;
+        }
+    }
+    function getError() {
+        return $this->errormsg;
+    }
+    function getCookies() {
+        return $this->cookies;
+    }
+    function getRequestURL() {
+        $url = 'http://'. $this->host;
+        if ($this->port != 80) {
+            $url .= ':'. $this->port;
+        }            
+        $url .= $this->path;
+        return $url;
+    }
+    // Setter methods
+    function setUserAgent($string) {
+        $this->user_agent = $string;
+    }
+    function setAuthorization($username, $password) {
+        $this->username = $username;
+        $this->password = $password;
+    }
+    function setCookies($array) {
+        $this->cookies = $array;
+    }
+    // Option setting methods
+    function useGzip($boolean) {
+        $this->use_gzip = $boolean;
+    }
+    function setPersistCookies($boolean) {
+        $this->persist_cookies = $boolean;
+    }
+    function setPersistReferers($boolean) {
+        $this->persist_referers = $boolean;
+    }
+    function setHandleRedirects($boolean) {
+        $this->handle_redirects = $boolean;
+    }
+    function setMaxRedirects($num) {
+        $this->max_redirects = $num;
+    }
+    function setHeadersOnly($boolean) {
+        $this->headers_only = $boolean;
+    }
+    function setDebug($boolean) {
+        $this->debug = $boolean;
+    }
+    // "Quick" static methods
+    function quickGet($url) {
+        $bits = parse_url($url);
+        $host = $bits['host'];
+        $port = isset($bits['port']) ? $bits['port'] : 80;
+        $path = isset($bits['path']) ? $bits['path'] : '/';
+        if (isset($bits['query'])) {
+            $path .= '?'. $bits['query'];
+        }
+        $client = new HttpClient($host, $port);
+        if (!$client->get($path)) {
+            return false;
+        } else {
+            return $client->getContent();
+        }
+    }
+    function quickPost($url, $data) {
+        $bits = parse_url($url);
+        $host = $bits['host'];
+        $port = isset($bits['port']) ? $bits['port'] : 80;
+        $path = isset($bits['path']) ? $bits['path'] : '/';
+        $client = new HttpClient($host, $port);
+        if (!$client->post($path, $data)) {
+            return false;
+        } else {
+            return $client->getContent();
+        }
+    }
+    function debug($msg, $object = false) {
+        if ($this->debug) {
+            print '<div style="border: 1px solid red; padding: 0.5em; margin: 0.5em;"><strong>HttpClient Debug:</strong> '.$msg;
+            if ($object) {
+                ob_start();
+                print_r($object);
+                $content = htmlentities(ob_get_contents());
+                ob_end_clean();
+                print '<pre>'. $content .'</pre>';
+            }
+            print '</div>';
+        }
+    }   
+}
+?>
\ No newline at end of file
diff --git a/blog/config.html b/blog/config.html
new file mode 100644 (file)
index 0000000..5408677
--- /dev/null
@@ -0,0 +1,153 @@
+<?php //$Id$ 
+/**
+ * config.html - 
+ * part of moodle blog module
+ *included from moodle/admin/blog.php. 
+ * site administrator site wide preference settings
+ */ ?>
+
+<form method="post" action="blog.php" name="form">
+<table cellpadding="9" cellspacing="0">
+<tr valign="top" align="center">
+    <td colspan="3" align="center">
+        <?php
+            print_string('helpadmin', 'blog');
+            print '&nbsp;&nbsp;';
+            helpbutton('admin', get_string('helpadmin', 'blog'), 'blog');
+        ?>
+    </td>
+</tr>
+<!--
+//templates removed from DB and placed in moodle/theme folder structure instead
+<tr valign="top">
+    <td align="right"><?php print_string('templateadministration', 'blog');?></strong>
+    </td>
+    <td colspan="2">
+        <a href="<?php echo $CFG->wwwroot;?>/blog/admin_tem.php">
+        <?php print_string('managetemplates', 'blog');?></a>
+    </td>
+</tr>
+-->
+<tr valign="top">
+    <td align="right">
+        <p><enable_trackback_in:</p>
+    </td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_enable_trackback_in', $CFG->blog_enable_trackback_in, '', '', ''); ?>
+    </td>
+    <td>
+        <?php print_string('enabletrackbackindesc', 'blog') ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right">
+        <p>blog_enable_pingback_in:</p>
+    </td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_enable_pingback_in', $CFG->blog_enable_pingback_in, '', '', ''); ?>
+    </td>
+    <td>
+        <?php print_string('enablepingbackindesc', 'blog') ?>
+    </td>
+</tr>
+
+<tr valign="top">
+    <td align="right">
+        <p>blog_enable_trackback_out:</p>
+    </td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_enable_trackback_out', $CFG->blog_enable_trackback_out, '', '', ''); ?>
+    </td>
+    <td>
+        <?php print_string('enabletrackbackoutdesc', 'blog') ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right">
+        <p>blog_enable_pingback_out:</p>
+    </td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_enable_pingback_out', $CFG->blog_enable_pingback_out, '', '', ''); ?>
+    </td>
+    <td>
+        <?php print_string('enablepingbackoutdesc', 'blog') ?>
+    </td>
+</tr>
+
+<tr valign="top">
+    <td align="right"><p>blog_useweblog_rpc:</td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_useweblog_rpc', $CFG->blog_useweblog_rpc, '', '', ''); ?>
+    </td>
+    <td>
+    <?php print_string('useweblogrpcdesc', 'blog') ?>
+    </td>
+</tr>
+<?php if (! isset($CFG->blog_default_fetch_num_entries) ) { ?>
+    <tr valign="top">
+        <td align="right"><p>blog_default_fetch_num_entries:</td>
+        <td>
+            <input name="blog_default_fetch_num_entries" type="text" size="5" value="<?php 
+            if (isset($CFG->blog_default_fetch_num_entries)) {
+                p($CFG->blog_default_fetch_num_entries);
+            }?>" />
+        </td>
+        <td>
+            Default number of entries to display per page 
+        <?php print_string('defaultfetchnumentriesdesc', 'blog') ?>
+        </td>
+    </tr>
+<?php } ?>
+<tr valign="top">
+    <td align="right"><p>blog_ratename:</td>
+    <td>
+    <input name="blog_ratename" type="text" size="10" value="<?php 
+    if (isset($CFG->blog_ratename)) {
+        p($CFG->blog_ratename);
+    }?>" />
+    </td>
+    <td>
+    <?php print_string('ratenamedesc', 'blog') ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right"><p>blog_anon:</td>
+    <td>
+    <input name="blog_anon" type="text" size="10" value="<?php 
+    if (isset($CFG->blog_anon)) {
+        p($CFG->blog_anon);
+    }?>" />
+    </td>
+    <td>
+    <?php print_string('anondesc', 'blog') ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right"><p>blog_default_title:</td>
+    <td>
+    <input name="blog_default_title" type="text" size="10" value="<?php 
+    if (isset($CFG->blog_default_title)) {
+        p($CFG->blog_default_title);
+    }?>" />
+    </td>
+    <td>
+    <?php print_string('defaulttitledesc', 'blog') ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right">
+        <p>blog_enable_moderation:</p>
+    </td>
+    <td>
+        <?php choose_from_menu ($BLOG_YES_NO_MODES, 'blog_enable_moderation', $CFG->blog_enable_moderation, '', '', ''); ?>
+    </td>
+    <td>
+        <?php print_string('enablemoderationdesc', 'blog') ?>
+    </td>
+</tr>
+<tr>
+    <td colspan="3" align="center">
+    <input type="submit" value="<?php print_string('savechanges') ?>"></td>
+</tr>
+</table>
+</form>
\ No newline at end of file
diff --git a/blog/edit.html b/blog/edit.html
new file mode 100755 (executable)
index 0000000..aaee0c7
--- /dev/null
@@ -0,0 +1,227 @@
+<?php //$Id$
+    // get the category drop down form element
+    $blogFilter =& new BlogFilter($userid, '', '', $post->courseid);
+    if (!isset($post->groupid)) {
+        $post->groupid = 0;
+    }
+
+    // find all the tags this post uses
+    if (isset($post->id)) {
+        if ($tagsused = get_records('blog_tag_instance', 'entryid', $post->postid)) {
+            foreach ($tagsused as $usedtag) {
+                $usedtags[] = $usedtag -> tagid;
+            }
+        }
+    }
+?>
+
+
+
+<!-- the following form is based on moodle/mod/forum/post.html -->
+
+<form name="entry" method="post" action="<?php echo $CFG->wwwroot;?>/blog/edit.php" id="entry" <?php echo $onsubmit; ?> enctype="multipart/form-data">
+
+<input type="hidden" name="realcourse" value="0" />
+<input type="hidden" name="realgroup" value="0" />
+
+<table border="0" cellpadding="5" id="edittable">
+<tr valign="top">
+    <td align="right">&nbsp;
+    </td>
+    <td colspan="2"><strong><?php echo $formHeading; ?></strong>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right"><strong><?php print_string('entrytitle', 'blog'); ?>:</strong></td>
+    <td colspan="2">
+        <input type="text" name="etitle" size="60" value="<?php p($post->etitle) ?>" id="etitle" />
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right">
+        <strong><?php print_string('publishto', 'blog'); ?>:</strong>
+    </td>
+    <td colspan="2">
+     <?php
+        $options = blog_applicable_publish_states($post->courseid); //$blogEntry may be null
+        choose_from_menu($options, 'publishstate', $post->publishstate, '');
+     ?>
+    <?php
+        helpbutton('publish_state', get_string('helppublish', 'blog'), 'blog');
+     ?>
+    </td>
+</tr>
+<tr>
+    <td>
+    <b>Official Tags</b>
+    </td>
+    
+    
+    <td>
+    <b>User Defined Tags</b>
+    </td>
+</tr>
+
+<tr>
+    <td>
+    <select name="otags[]" multiple="multiple" size="8">
+    <?php
+        $otags = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'official\' ORDER by text ASC');
+        foreach ($otags as $otag) {
+            if (in_array($otag->id, $usedtags)) {
+                echo '<option value="'.$otag->id.'" selected="selected">'.$otag->text.'</option>';
+            } else {
+                echo '<option value="'.$otag->id.'">'.$otag->text.'</option>';
+            }
+        }
+    ?>
+    </select>
+    </td>
+
+
+    <td>
+    <select name="ptags[]" multiple="multiple" size="8">
+    <?php
+        $ptags = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'personal\' ORDER by text ASC');
+        foreach ($ptags as $ptag) {
+            if (in_array($ptag->id, $usedtags)) {
+                echo '<option value="'.$ptag->id.'" selected="selected">'.$ptag->text.'</option>';
+            } else {
+                echo '<option value="'.$ptag->id.'">'.$ptag->text.'</option>';
+            }
+        }
+    ?>
+    </select>
+    </td>
+</tr>
+
+
+<tr>
+    <td>
+    <?php
+    link_to_popup_window($CFG->wwwroot."//blog/tags.php",'popup',get_string('tagmanagement','blog'));
+    ?>
+    </td>
+</tr>
+<tr valign="top">
+    <td align="right"><strong>
+    <?php 
+        if (isset($post->useextendedbody) && $post->useextendedbody) {
+            print_string('entryexcerpt', 'blog'); 
+        } else {
+            print_string('entrybody', 'blog');         
+        }
+    ?>:
+    </strong><br /><br />
+    <small><small>
+    <?php
+        helpbutton('reading', get_string('helpreading'), 'moodle', true, true);
+        echo '<br />';
+        helpbutton('writing', get_string('helpwriting'), 'moodle', true, true);
+        echo '<br />';
+        helpbutton('questions', get_string('helpquestions'), 'moodle', true, true);
+        echo '<br />';
+        if ($usehtmleditor) {
+           helpbutton('richtext', get_string('helprichtext'), 'moodle', true, true);
+        } else {
+           emoticonhelpbutton('entry', 'body');
+        }
+    ?>     
+    </small></small>              
+    </td>
+    <td align="left" colspan="2">
+    <?php
+        if (isset($post->useextendedbody) && $post->useextendedbody) {
+            print_string('entrybodydesc', 'blog');
+        } else {
+            print_string('entrybodyonlydesc', 'blog');
+        }
+        print '<br />'."\n";
+        // usage: print_textarea($usehtmleditor, $rows, $cols, $width, $height, $name, $value="", $courseid)
+        print_textarea($usehtmleditor, 6, 60, 600, 500, 'body', $post->body, $post->courseid); ?>
+    </td>
+</tr>
+<?php  
+    if (isset($post->useextendedbody) && $post->useextendedbody) {
+        print '<tr valign=top>'."\n";
+        print '<td align=right><strong>'."\n";
+        print_string('entryextendedbody', 'blog');
+        print ':</strong></td>'. "\n";
+        print '<td align=left colspan=2>'."\n";
+        print_string('entryextendedbodydesc', 'blog');
+        print '<br />'."\n";
+        print_textarea($usehtmleditor, 25, 60, 600, 500, 'extendedbody', $post->extendedbody, $post->courseid);
+        print '</td></tr>';
+   } else { 
+        print '<input type="hidden" name="extendedbody" value="" />';
+   }
+
+?>
+<tr valign="top">
+    <td align="right"><strong><?php print_string('formattexttype'); ?>:</strong></td>
+    <td colspan="2">
+    <?php
+       choose_from_menu(format_text_menu(), 'format', $post->format, '');
+     ?>
+    <small><small>
+    <?php
+        helpbutton('textformat', get_string('helpformatting'));
+     ?>
+    </small></small>
+    </td>
+</tr>
+
+    <?php
+    if ($CFG->blog_enable_trackback_out || $CFG->blog_enable_pingback_out) {
+        print '<tr valign="top">';
+        print '<td align="right"><strong>';
+        print_string('advancedoptions', 'blog');
+        print '</strong></td><td align="left">';
+        
+
+         if ($CFG->blog_enable_pingback_out) {
+             print '<input type="checkbox" name="sendpingbacks" value="1" id="sendpingpacks" /><small><small>';      
+             print_string('sendpingback', 'blog');     
+             helpbutton('pingback', get_string('helppingback', 'blog'), 'blog');
+             print '</small></small><br />';       
+         }
+
+          if ($CFG->blog_enable_trackback_out) {
+              print '<input type="checkbox" name="sendtrackbacks" value="1" id="sendtrackbacks" /><small><small>';  
+              print_string('sendtrackback', 'blog');
+              helpbutton('trackback', get_string('helptrackback', 'blog'), 'blog');
+              print '</small></small><br />';
+          }
+
+        print '</td></tr>';
+    }
+    ?>
+<tr>
+    <td align="center" colspan="3">
+        <input type="hidden" name="editform" value="1" id="editform" />
+        <input type="hidden" name="courseid" value="<?php p($post->courseid) ?>" id="courseid" />
+        <?php 
+            //groups not supported quite yet - pseudocode:        
+            /*if ( isset($post->groupid) ) {
+                print '<input type="hidden" name="groupid" value="'. p($post->groupid) .'" />';
+            }*/
+        ?>
+        <input type="hidden" name="userid" value="<?php p($post->userid) ?>" id="userid" />
+        <input type="hidden" name="userid" value="<?php echo $userid; ?>" id="userid" />
+        <input type="hidden" name="tem" id="tem" />
+<?php
+    if (isset($post->postid) && ($post->postid != -1) ) {
+    ?>
+        <input type="hidden" name="postid" value="<?php echo $post->postid; ?>" id="postid" />
+        <input type="hidden" name="act" value="update" id="act" />
+        <input type="submit" value="Update" id="Submit1" name="Submit1" />&nbsp;
+        <input type="button" value="Cancel" onclick="javascript:history.go(-1)" id="cancel" name="cancel" />
+<?php
+    } else { ?>
+        <input type="hidden" name="act" value="save" id="act" />
+        <input type="submit" value="<?php print_string('savechanges'); ?>" id="savechanges" name="Submit2" />&nbsp;
+<?php } ?>
+    </td>
+</tr>
+</table>
+</form>
diff --git a/blog/edit.php b/blog/edit.php
new file mode 100755 (executable)
index 0000000..ba13664
--- /dev/null
@@ -0,0 +1,361 @@
+<?php //$Id$
+
+require_once('../config.php');
+include_once('lib.php');
+include_once('class.BlogInfo.php');
+require_login();
+// detemine where the user is coming from in case we need to send them back there
+if (isset($_SERVER['HTTP_REFERER'])) {
+    $referrer = $_SERVER['HTTP_REFERER'];
+} else {
+    $referrer = $CFG->wwwroot;
+}
+
+//first verify that user is not a guest
+if (isguest()) {
+    error(get_string('noguestpost', 'forum'), $referrer);
+}
+
+optional_variable($userid, 0);
+optional_variable($editid, '');
+optional_variable($sendpingbacks, 0);
+optional_variable($sendtrackbacks, 0);
+
+global $USER, $CFG;
+
+//check to see if there is a requested blog to edit
+if (!empty($userid) && $userid != 0) {
+    if (blog_isLoggedIn() && $userid == $USER->id ) {
+        ; // Daryl Hawes note: is this a placeholder for missing functionality?
+    }
+} else if ( blog_isLoggedIn() ) {
+    //the user is logged in and have not specified a blog - so they will be editing their own
+    $tempBlogInfo = blog_user_bloginfo();
+    $userid = $tempBlogInfo->userid;
+    unset($tempBlogInfo); //free memory from temp object - bloginfo will be created again in the included header
+} else {
+    error(get_string('noblogspecified', 'blog') .'<a href="'. $CFG->blog_blogurl .'">' .get_string('viewentries', 'blog') .'</a>');
+}
+
+$pageNavigation = 'edit';
+
+include($CFG->dirroot .'/blog/header.php');
+
+if (!empty($course)) {
+    $courseid = $course->id;
+} else if (!isadmin() && $CFG->blog_enable_moderation) {
+    // the user is not an admin, blog moderation is on and there is no course association
+    //Daryl Hawes note: possible bug here if editing a personal post that existed before blog moderation was enabled for the site.
+    error('Blog moderation is enabled. Your entries must be associated with a course.');
+}
+
+//print_object($PAGE->bloginfo); //debug
+
+//check if user is in blog's acl
+if ( !blog_user_has_rights($PAGE->bloginfo) ) {
+    if ($editid != '') {
+        $blogEntry = $PAGE->bloginfo->get_blog_entry_by_id($editid);
+        if (! (isteacher($blogEntry->$entryCourseId)) ) {
+//            error( get_string('notallowedtoedit'.' You do not teach in this course.', 'blog'), $CFG->wwwroot .'/login/index.php');
+            error( get_string('notallowedtoedit', 'blog'), $CFG->wwwroot .'/login/index.php');
+        }
+    } else {
+        error( get_string('notallowedtoedit', 'blog'), $CFG->wwwroot .'/login/index.php');
+    }
+}
+
+//////////// SECURITY AND SETUP COMPLETE - NOW PAGE LOGIC ///////////////////
+
+if (isset($act) && $act == 'del')
+{
+    require_variable($postid);
+    do_delete($PAGE->bloginfo, $postid);
+}
+if ($usehtmleditor = can_use_richtext_editor()) {
+    $defaultformat = FORMAT_HTML;
+    $onsubmit = '';
+} else {
+    $defaultformat = FORMAT_MOODLE;
+    $onsubmit = '';
+}
+
+if ($post = data_submitted( get_referer() ) ) {
+    if (!empty($post->editform)) { //make sure we're processing the edit form here
+        //print_object($post); //debug
+
+        ///these varaibles needs to be changed because of the javascript hack
+        ///post->courseid
+        ///post->groupid
+        $post->courseid = $post->realcourse;   //might not need either, if javascript re-written
+        $post->groupid = $post->realgroup;   //might not need
+        $courseid = $post->realcourse;
+        //end of yu's code
+        
+        if (!$post->etitle or !$post->body) {
+            $post->error = get_string('emptymessage', 'forum');
+        }
+        if ($post->act == 'save') {
+            do_save($post, $PAGE->bloginfo, $sendpingbacks, $sendtrackbacks);
+        } else if ($post->act == 'update') {
+            do_update($post, $PAGE->bloginfo, $sendpingbacks, $sendtrackbacks);
+        } else if ($post->act == 'del') {
+            require_variable($postid);
+            do_delete($PAGE->bloginfo, $postid);
+        }
+    }
+} else {
+
+    //no post data yet, so load up the post array with default information
+    $post->etitle = '';
+    $post->userid = $USER->id;
+    $post->body = '';
+    $post->extendedbody = '';
+    $post->useextendedbody = $PAGE->bloginfo->get_blog_use_extended_body();
+    $post->format = $defaultformat;
+    $post->categoryid = array(1);
+    $post->publishstate = 'draft';
+    $post->courseid  = $courseid;
+    
+
+}
+
+if ($editid != '') {  // User is editing a post
+    // ensure that editing is allowed first - admin users can edit any posts
+    if (!isadmin() && $CFG->blog_enable_moderation && $blogEntry->entryPublishState != 'draft') {
+        error('You are not allowed to modify a published entry. A teacher must first change this post back to draft status.'); //Daryl Hawes note: localize this line
+    }
+    $blogEntry = $PAGE->bloginfo->get_blog_entry_by_id($editid);
+
+    //using an unformatted entry body here so that extra formatting information is not stored in the db
+    $post->body = $blogEntry->get_unformatted_entry_body();
+    $post->extendedbody = $blogEntry->get_unformatted_entry_extended_body();
+    $post->useextendedbody = $PAGE->bloginfo->get_blog_use_extended_body();
+    $post->etitle = $blogEntry->entryTitle;    
+    $post->postid = $editid;
+    $post->userid = $PAGE->bloginfo->userid;
+    $post->categoryid = $blogEntry->entryCategoryIds;
+    $post->format = $blogEntry->entryFormat;
+    $post->publishstate = $blogEntry->entryPublishState;
+    $post->courseid  = $blogEntry->entryCourseId;
+    $post->groupid = (int)$blogEntry->entryGroupId;
+}
+
+if (isset($post->postid) && ($post->postid != -1) ) {
+    $formHeading = get_string('updateentrywithid', 'blog') . $post->postid;
+} else {
+    $formHeading = get_string('addnewentry', 'blog');
+}
+
+if (isset($post->error)) {
+    notify($post->error);
+}
+
+print_simple_box_start("center");
+require('edit.html');
+print_simple_box_end();
+
+    // Janne comment: Let's move this in here
+    // so IE gets more time to load the
+    // Page.
+    if ($usehtmleditor) {
+        // Janne comment: there are two text fields in form
+        // so lets try to replace them both with
+        // HTMLArea editors
+        use_html_editor();
+    }
+
+include($CFG->dirroot .'/blog/footer.php');
+
+
+/*****************************   edit.php functions  ***************************/
+/*
+* do_delete
+* takes $bloginfo_arg argument as reference to a blogInfo object.
+* also takes the postid - the id of the entry to be removed
+*/
+function do_delete(&$bloginfo_arg, $postid) {
+    global $CFG;
+    // make sure this user is authorized to delete this entry.
+    // cannot use $post->pid because it may not have been initialized yet. Also the pid may be in get format rather than post.
+    if ($bloginfo_arg->delete_blog_entry_by_id($postid)) {
+        //echo "bloginfo_arg:"; //debug
+        print_object($bloginfo_arg); //debug
+        //echo "pid to delete:".$postid; //debug
+        delete_records('blog_tag_instance', 'entryid', $postid);
+        print '<strong>'. get_string('entrydeleted', 'blog') .'</strong><p>';
+
+        //record a log message of this entry deletion
+        if ($site = get_site()) {
+            add_to_log($site->id, 'blog', 'delete', 'index.php?userid='. $bloginfo_arg->userid, 'deleted blog entry with entry id# '. $postid);
+        }
+    } else {
+        error(get_string('entryerrornotyours', 'blog'));
+    }
+
+    //comment out this redirect to debug the deletion of entries
+    redirect($CFG->wwwroot .'/blog/index.php?userid='. $bloginfo_arg->userid);
+}
+
+/**
+*  do_save
+*
+* @param object $post argument is a reference to the post object which is used to store information for the form
+* @param object $bloginfo_arg argument is reference to a blogInfo object.
+*/
+function do_save(&$post, &$bloginfo_arg, $sendpingbacks, $sendtrackbacks) {
+    global $USER, $CFG;
+//    echo 'Debug: Post object in do_save function of edit.php<br />'; //debug
+//    print_object($post); //debug
+
+    if ($post->body == '') {
+        $post->error =  get_string('nomessagebodyerror', 'blog');
+    } else {
+
+        //initialize courseid and groupid if specified
+        if (isset($post->courseid)) {
+            $courseid = $post->courseid;
+        } else {
+            $courseid = 1;
+        }
+        if (isset($post->groupid)) {
+            $groupid = $post->groupid;
+        } else {
+            $groupid = '';
+        }
+       
+/*     
+        //group pseudocode 
+        if ($groupid != '') {
+            if (! ismember($post->groupid) ) {
+                error('You are not a member of the specified group. Group with id#('.$groupid.')'); //Daryl Hawes note: LOCALIZATION NEEDED FOR THIS LINE
+            }
+        }*/
+
+        // Insert the new blog entry.
+        $entryID = $bloginfo_arg->insert_blog_entry($post->etitle, $post->body, $post->extendedbody, $USER->id, $post->format, $post->publishstate, $courseid, $groupid);
+
+//        print 'Debug: created a new entry - entryId = '.$entryID.'<br />'; //debug
+//        echo 'Debug: do_save() in edit.php calling blog_do_*back_pings<br />'."\n"; //debug
+        $otags = optional_param('otags');
+        $ptags = optional_param('ptags');
+        // Add tags information
+        foreach ($otags as $otag) {
+            $tag->entryid = $entryID;
+            $tag->tagid = $otag;
+            $tag->groupid = $groupid;
+            $tag->courseid = $courseid;
+            $tag->userid = $USER->id;
+
+            insert_record('blog_tag_instance',$tag);
+        }
+        
+        foreach ($ptags as $ptag) {
+            $tag->entryid = $entryID;
+            $tag->tagid = $ptag;
+            $tag->groupid = $groupid;
+            $tag->courseid = $courseid;
+            $tag->userid = $USER->id;
+
+            insert_record('blog_tag_instance',$tag);
+        }
+
+        print '<strong>'. get_string('entrysaved', 'blog') .'</strong><br />';
+        //record a log message of this entry addition
+        if ($site = get_site()) {
+            add_to_log($site->id, 'blog', 'add', 'archive.php?userid='. $bloginfo_arg->userid .'&postid='. $entryID, 'created new blog entry with entry id# '. $entryID);
+        }
+        //to debug this save function comment out the following redirect code
+        if ($courseid == 1 || $courseid == 0 || $courseid == '') {
+            redirect($CFG->wwwroot .'/blog/index.php?userid='. $bloginfo_arg->userid);
+        } else {
+            redirect($CFG->wwwroot .'/course/view.php?id='. $courseid);
+        }
+    }
+}
+
+/**
+ * @param . $post argument is a reference to the post object which is used to store information for the form
+ * @param . $bloginfo_arg argument is reference to a blogInfo object.
+ * @todo complete documenting this function. enable trackback and pingback between entries on the same server
+ */
+function do_update(&$post, &$bloginfo, $sendpingbacks, $sendtrackbacks) {
+
+    global $CFG, $USER;
+    
+    //initialize courseid and groupid if specified
+    if (isset($post->courseid)) {
+        $courseid = $post->courseid;
+    } else {
+        $courseid = 1;
+    }
+    if (isset($post->groupid)) {
+        $groupid = $post->groupid;
+    } else {
+        $groupid = '';
+    }
+
+/*
+    //pseudocode for handling groups
+    if ($groupid != '') {
+        if (! ismember($groupid) ) {
+            error('You are not a member of the specified group. Group with id#('. $groupid .')'); //Daryl Hawes note: LOCALIZATION NEEDED FOR THIS LINE
+        }
+    }*/
+    
+    $blogentry = $bloginfo->get_blog_entry_by_id($post->postid);
+    echo "id id ".$post->postid;
+//  print_object($blogentry);  //debug
+
+    $blogentry->set_title($post->etitle);
+    $blogentry->set_body($post->body);
+    if (isset($post->extendedbody)) {
+        $blogentry->set_extendedbody($post->extendedbody);
+    }
+    $blogentry->set_format($post->format);
+    $blogentry->set_publishstate($post->publishstate); //we don't care about the return value here
+    $blogentry->set_courseid($courseid);
+    $blogentry->set_groupid($groupid);
+
+    if ( !$error = $blogentry->save() ) {
+//        echo 'Debug: do_update in edit.php calling do_pings<br />'."\n"; //debug
+        delete_records('blog_tag_instance', 'entryid', $blogentry->entryId);
+
+        $otags = optional_param('otags');
+        $ptags = optional_param('ptags');
+        // Add tags information
+        foreach ($otags as $otag) {
+            $tag->entryid = $blogentry->entryId;
+            $tag->tagid = $otag;
+            $tag->groupid = $groupid;
+            $tag->courseid = $courseid;
+            $tag->userid = $USER->id;
+
+            insert_record('blog_tag_instance',$tag);
+        }
+
+        foreach ($ptags as $ptag) {
+            $tag->entryid = $blogentry->entryId;
+            $tag->tagid = $ptag;
+            $tag->groupid = $groupid;
+            $tag->courseid = $courseid;
+            $tag->userid = $USER->id;
+
+            insert_record('blog_tag_instance',$tag);
+        }
+        // only do pings if the entry is published to the world
+        // Daryl Hawes note - eventually should check if it's on the same server
+        // and if so allow pb/tb as well - especially now that moderation is in place
+        print '<strong>'. get_string('entryupdated', 'blog') .'</strong><p>';
+
+        //record a log message of this entry update action
+        if ($site = get_site()) {
+            add_to_log($site->id, 'blog', 'update', 'archive.php?userid='. $bloginfo->userid .'&postid='. $post->postid, 'updated existing blog entry with entry id# '. $post->postid);
+        }
+
+        redirect($CFG->wwwroot .'/blog/index.php?userid='. $bloginfo->userid);
+    } else {
+//        get_string('', 'blog') //Daryl Hawes note: localize this line
+        $post->error =  'There was an error updating this post in the database: '. $error;
+    }
+}
+?>
diff --git a/blog/footer.php b/blog/footer.php
new file mode 100644 (file)
index 0000000..cd35640
--- /dev/null
@@ -0,0 +1,28 @@
+                </td>
+            </tr>
+        </table>
+    </td>
+<?php 
+print '<!-- End page content -->'."\n";
+
+// The right column
+if (blocks_have_content($pageblocks, BLOCK_POS_RIGHT) || $editing) {
+    echo '<td style="vertical-align: top; width: '. $preferred_width_right .'px;">';
+    echo '<!-- Begin right side blocks -->'."\n";
+    blocks_print_group($PAGE, $pageblocks, BLOCK_POS_RIGHT);
+    print_spacer(1, 120, true);
+    echo '<!-- End right side blocks -->'."\n";
+    echo '</td>';
+}
+?>
+
+    </tr>
+</table>
+
+<?php
+if (isset($course)) {
+    print_footer($course);
+} else {
+    print_footer();
+}
+?>
\ No newline at end of file
diff --git a/blog/header.php b/blog/header.php
new file mode 100755 (executable)
index 0000000..29f10e5
--- /dev/null
@@ -0,0 +1,237 @@
+<?php //$Id$
+
+global $CFG, $USER;
+
+require_once($CFG->dirroot .'/blog/lib.php');
+require_once($CFG->libdir .'/pagelib.php');
+require_once($CFG->dirroot .'/blog/blogpage.php');
+require_once($CFG->libdir .'/blocklib.php');
+require_once($CFG->dirroot .'/course/lib.php');
+
+optional_param('blockaction');
+optional_param('instanceid', 0, PARAM_INT);
+optional_param('blockid',    0, PARAM_INT);
+optional_param('groupid',    0, PARAM_INT);
+optional_param('userid',     0, PARAM_INT);
+
+optional_variable($categoryid, '');
+optional_variable($groupid, '');
+optional_variable($pageTitle, '');
+optional_variable($pageHeading, ''/*$site->fullname*/);
+optional_variable($pageFocus, '');
+optional_variable($pageMeta, '');
+optional_variable($pageNavigation, '');
+
+if (!isset($courseid)) {
+    $courseid = optional_param('courseid', SITEID, PARAM_INT);
+}
+
+//header('X-Pingback: '. $CFG->wwwroot .'/blog/api.php'."\n");
+
+if (!$site = get_site()) {
+    redirect($CFG->wwwroot.'/index.php');
+}
+
+if ($courseid == 0 ) {
+    $courseid = SITEID;
+}
+
+// now check that they are logged in and allowed into the course (if specified)
+if ($courseid != SITEID) {
+    if (! $course = get_record('course', 'id', $courseid)) {
+        error('The course number was incorrect ('. $courseid .')');
+    }
+    require_login($course->id);
+}
+
+// ensure that if a group is specified that the user is in fact a member of that group
+/*
+if ($groupid) {
+    if (!ismember($groupid) && !isteacher($course->id)) {
+        error('You are not a member of the specified group. Group with id#('. $groupid .')');
+    }
+}*/
+
+// Bounds for block widths within this page
+define('BLOCK_L_MIN_WIDTH', 160);
+define('BLOCK_L_MAX_WIDTH', 210);
+define('BLOCK_R_MIN_WIDTH', 160);
+define('BLOCK_R_MAX_WIDTH', 210);
+
+//_____________ new page class code ________
+$pagetype = PAGE_BLOG_VIEW;
+$pageclass = 'page_blog';
+
+// map our page identifier to the actual name
+// of the class which will be handling its operations.
+page_map_class($pagetype, $pageclass);    
+
+// Now, create our page object.
+if (!isset($USER->id)) {
+    $PAGE = page_create_object($pagetype);
+}
+else {
+    $PAGE = page_create_object($pagetype, $USER->id);
+}
+$PAGE->courseid = $courseid;
+$PAGE->init_full(); //init the BlogInfo object and the courserecord object
+
+if (isset($tagid)) {
+    $taginstance = get_record('tags', 'id', $tagid);
+} else {
+    $tagid = '';
+}
+if (!isset($filtertype)) {
+    $filtertype = 'user';
+    $filterselect = $USER->id;
+}
+
+$blogstring = get_string('blogs','blog');
+$tagstring = get_string('tag','blog');
+
+switch ($filtertype) {
+    case 'site':
+        if ($tagid) {
+            print_header("$site->shortname: $blogstring", "$site->fullname",
+                        '<a href="index.php?filtertype=site">'. "$blogstring</a> -> $tagstring: $taginstance->text",'','',true,$PAGE->get_extra_header_string());
+        } else {
+            print_header("$site->shortname: $blogstring", "$site->fullname",
+                        "$blogstring",'','',true,$PAGE->get_extra_header_string());
+        }
+    break;
+
+    case 'course':
+        if ($tagid) {
+            print_header("$course->shortname: $blogstring", "$course->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$filterselect.'">'.$course->shortname.'</a> ->
+                        <a href="index.php?filtertype=course&amp;filterselect='.$filterselect.'">'. "$blogstring</a> -> $tagstring: $taginstance->text",'','',true,$PAGE->get_extra_header_string());
+        } else {
+            print_header("$site->shortname: $blogstring", "$site->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$filterselect.'">'.$course->shortname."</a> ->
+                        $blogstring",'','',true,$PAGE->get_extra_header_string());
+        }
+    break;
+
+    case 'group':
+
+        $thisgroup = get_record('groups', 'id', $filterselect);
+
+        if ($tagid) {
+            print_header("$course->shortname: $blogstring", "$course->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/index.php?id='.$course->id.'&amp;group='.$filterselect.'">'.$thisgroup->name.'</a> ->
+                        <a href="index.php?filtertype=group&amp;filterselect='.$filterselect.'">'. "$blogstring</a> -> $tagstring: $taginstance->text",'','',true,$PAGE->get_extra_header_string());
+        } else {
+            print_header("$course->shortname: $blogstring", "$course->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/index.php?id='.$course->id.'&amp;group='.$filterselect.'">'.$thisgroup->name."</a> ->
+                        $blogstring",'','',true,$PAGE->get_extra_header_string());
+
+        }
+    
+    break;
+
+    case 'user':
+        $user = get_record('user', 'id', $filterselect);
+        $participants = get_string('participants');
+
+        if ($course->id && $course->id != SITEID) {
+            if ($tagid) {
+                print_header("$course->shortname: $blogstring", "$course->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/index.php?id='.$course->id.'">'.$participants.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/view.php?id='.$filterselect.'&amp;course='.$course->id.'">'.fullname($user).'</a> ->
+                        <a href="index.php?courseid='.optional_param('courseid').'&amp;filtertype=user&amp;filterselect='.$filterselect.'">'. "$blogstring</a> -> $tagstring: $taginstance->text",'','',true,$PAGE->get_extra_header_string());
+
+            } else {
+                print_header("$course->shortname: $blogstring", "$course->fullname",
+                        '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/index.php?id='.$course->id.'">'.$participants.'</a> ->
+                        <a href="'.$CFG->wwwroot.'/user/view.php?id='.$filterselect.'&amp;course='.$course->id.'">'.fullname($user).'</a> ->
+                        '.$blogstring,'','',true,$PAGE->get_extra_header_string());
+
+            }
+
+        }
+        //in top view
+        else {
+
+            if ($tagid) {
+                print_header("$site->shortname: $blogstring", "$site->fullname",
+                        '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$filterselect.'">'.fullname($user).'</a> ->
+                        <a href="index.php?filtertype=user&amp;filterselect='.$filterselect.'">'. "$blogstring</a> -> $tagstring: $taginstance->text",'','',true,$PAGE->get_extra_header_string());
+
+            } else {
+                print_header("$site->shortname: $blogstring", "$site->fullname",
+                        '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$filterselect.'">'.fullname($user).'</a> ->
+                        '.$blogstring,'','',true,$PAGE->get_extra_header_string());
+
+            }
+           
+        }
+    break;
+
+    default:    //user click on add from block
+        print_header("$site->shortname: $blogstring", "$site->fullname",
+                        '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$filterselect.'&amp;course='.$course->id.'">'.fullname($user).'</a> ->
+                        '.$blogstring,'','',true,$PAGE->get_extra_header_string());
+    break;
+}
+
+
+/// site blogs - sitefullname -> blogs -> (?tag)
+/// course blogs - sitefullname -> course fullname ->blogs ->(?tag)
+/// group blogs - sitefullname -> course fullname ->group ->(?tag)
+/// user blogs - sitefullname -> (?coursefullname) -> participants -> blogs -> (?tag)
+
+$editing = false;
+if ($PAGE->user_allowed_editing()) {
+    $editing = $PAGE->user_is_editing();
+}
+
+// Calculate the preferred width for left, right and center (both center positions will use the same)
+optional_variable($preferred_width_left,  blocks_preferred_width($pageblocks[BLOCK_POS_LEFT]));
+optional_variable($preferred_width_right, blocks_preferred_width($pageblocks[BLOCK_POS_RIGHT]));
+$preferred_width_left = min($preferred_width_left, BLOCK_L_MAX_WIDTH);
+$preferred_width_left = max($preferred_width_left, BLOCK_L_MIN_WIDTH);
+$preferred_width_right = min($preferred_width_right, BLOCK_R_MAX_WIDTH);
+$preferred_width_right = max($preferred_width_right, BLOCK_R_MIN_WIDTH);
+
+// Display the blocks and allow blocklib to handle any block action requested
+$pageblocks = blocks_get_by_page($PAGE);
+
+if ($editing) {
+    if (!empty($blockaction) && confirm_sesskey()) {
+        if (!empty($blockid)) {
+            blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), intval($blockid));
+        } else if (!empty($instanceid)) {
+            $instance = blocks_find_instance($instanceid, $pageblocks);
+            blocks_execute_action($PAGE, $pageblocks, strtolower($blockaction), $instance);
+        }
+        // This re-query could be eliminated by judicious programming in blocks_execute_action(),
+        // but I'm not sure if it's worth the complexity increase...
+        $pageblocks = blocks_get_by_page($PAGE);
+    }
+    $missingblocks = blocks_get_missing($PAGE, $pageblocks);
+}
+
+/// Layout the whole page as three big columns.
+print '<table border="0" cellpadding="3" cellspacing="0" width="100%">' . "\n";
+print '<tr valign="top">' . "\n";
+
+/// The left column ...
+if (blocks_have_content($pageblocks, BLOCK_POS_LEFT) || $editing) {
+    print '<td style="vertical-align: top; width: '. $preferred_width_left .'px;">' . "\n";
+    print '<!-- Begin left side blocks -->' . "\n";
+    blocks_print_group($PAGE, $pageblocks, BLOCK_POS_LEFT);
+    print '<!-- End left side blocks -->' . "\n";
+    print '</td>' . "\n";
+}
+
+/// Start main column
+print '<!-- Begin page content -->' . "\n";
+print '<td width="*">';
+?>
+<table width="100%">
+<tr>
+<td height="100%" valign="top">
diff --git a/blog/index.php b/blog/index.php
new file mode 100755 (executable)
index 0000000..e346054
--- /dev/null
@@ -0,0 +1,198 @@
+<?php // $Id$
+
+/**
+ * file index.php
+ * index page to view blogs. if no blog is specified then site wide entries are shown
+ * if a blog id is specified then the latest entries from that blog are shown
+ */
+
+if (!file_exists('../config.php')) {
+    header('Location: ../install.php');
+    die;
+}
+require_once('../config.php');
+
+require_once($CFG->dirroot .'/blog/lib.php');
+require_once($CFG->libdir .'/blocklib.php');
+
+$id = optional_param('id');
+$limit = optional_param('limit');
+optional_variable($formstart, 'none');
+optional_variable($m, ''); //month
+optional_variable($y, ''); //year
+optional_variable($d, ''); //day
+optional_variable($limit, 'none');
+optional_variable($formstart, 'none');
+
+$userid = optional_param('userid',0,PARAM_INT);
+$groupid = optional_param('groupid',0,PARAM_INT);
+$courseid = optional_param('courseid',0,PARAM_INT);
+$tag = optional_param('tag');
+$tagid = optional_param('tagid');
+$filtertype = optional_param('filtertype','',PARAM_ALPHA);
+$filterselect = optional_param('filterselect','',PARAM_NOTAGS);
+
+/// overwrite filter code here
+/// the the following code does the rights checkings?
+
+if ($filtertype) {
+    switch ($filtertype) {
+
+        case 'site':
+            if ($filterselect) {
+                $userid = $filterselect;
+            } else {
+                $userid = 0;
+            }
+            $course = get_site();
+            $courseid = SITEID;
+        break;
+
+        case 'course':
+            if ($filterselect) {
+                $courseid = $filterselect;
+                $course = get_record('course','id',$courseid);
+            }
+            $userid =0;
+            $groupid = 0;
+            
+        break;
+
+        case 'group':
+            if ($filterselect) {
+                $groupid = $filterselect;
+                $group = get_record('groups','id',$groupid);
+                $course = get_record('course','id',$group->courseid);
+                $courseid = $course->id;
+            } else {
+                $groupid = 0;
+            }
+            $userid = 0;
+            
+        break;
+
+        case 'user':
+            if ($filterselect) {
+                $userid = $filterselect;
+            }
+            $groupid = 0;
+
+        break;
+        default:
+        break;
+    }
+
+} else if ($userid) {    //default to user
+    $filtertype = 'user';
+    $filterselect = $userid;
+} else {
+    $filtertype = 'site';
+    $filterselect = '';
+}
+
+
+/// rights checking
+
+switch ($filtertype) {
+    case 'site':
+        if ($CFG->bloglevel < 4) {
+            error ('site blogs is not enabled');
+        } else if ($CFG->bloglevel < 5) {
+            require_login();
+        }
+    break;
+    case 'course':
+        if ($CFG->bloglevel < 3) {
+            error ('course blogs is not enabled');
+        }
+
+        if (!isstudent($filterselect) && !isteacher($filterselect)) {
+            error ('you must be a student in this course to view course blogs');
+        }
+        /// check if viewer is student
+    break;
+    case 'group':
+        if ($CFG->bloglevel < 2) {
+            error ('group blogs is not enabled');
+        }
+        if (!isteacheredit($course) and (groupmode($course) == SEPARATEGROUPS)) {
+            if (!ismember($filterselect)) {
+                error ('you are not in this group');
+            }
+        }
+        /// check if user is editting teacher, or if spg, is member
+    break;
+    case 'user':
+        if ($CFG->bloglevel < 1) {
+            error ('Blogs is not enabled');
+        }
+        $canview = 0;    //bad start
+        
+        $usercourses = get_my_courses($filterselect);
+        foreach ($usercourses as $usercourse) {
+            /// if viewer and user sharing same non-spg course, then grant permission
+            if (groupmode($usercourse)!= SEPARATEGROUPS){
+                if (isstudent($usercourse->id) || isteacher($usercourse->id)) {
+                    $canview = 1;
+                }
+            } else {
+                /// now we need every group the user is in, and check to see if view is a member
+                if ($usergroups = user_group($usercourse->id, $filterselect)) {
+                    foreach ($usergroups as $usergroup) {
+                        if (ismember($usergroup->id)) {
+                            $canview = 1;
+                        }
+                    }
+                }
+            }
+        }
+        if (!$canview && $CFG->bloglevel < 4) {
+            error ('you can not view this user\'s blogs');
+        }
+        /// check to see if the viewer is sharing no_group, visible group course.
+        /// if not , check if the viewer is in any spg group as the user
+    break;
+    default:
+    break;
+}
+
+
+//first set the start and end day equal to the day argument passed in from the get vars
+$startday = $d;
+$endday = $d + 1;
+if ( empty($d) && !empty($m) && !empty($y) ) {
+    //if there was no day specified then the entire month is wanted.
+    $startday = 1;
+    $endday = blog_mk_getLastDayofMonth($m, $y);
+}
+
+if ($limit == 'none') {
+    $limit = get_user_preferences('blogpagesize',8);
+}
+       
+if ($formstart == 'none' || $formstart < 0) {
+    $start = 0;
+} else {
+    $start = $formstart;
+}
+
+$blogFilter =& new BlogFilter($userid, '', $courseid, $groupid, $limit, $start, $m, $startday, $y, $m, $endday, $y,$filtertype, $filterselect, $tagid, $tag);
+//print_object($blogFilter); //debug
+
+$pageNavigation = '';
+
+include($CFG->dirroot .'/blog/header.php');
+
+//prints the tabs
+$currenttab = 'blogs';
+$user = $USER;
+if (!$course) {
+    $course = get_record('course','id',optional_param('courseid', SITEID, PARAM_INT));
+}
+require_once($CFG->dirroot .'/user/tabs.php');
+
+blog_print_html_formatted_entries($blogFilter, $filtertype, $filterselect);
+
+include($CFG->dirroot .'/blog/footer.php');
+
+?>
diff --git a/blog/lib.php b/blog/lib.php
new file mode 100755 (executable)
index 0000000..e545b0b
--- /dev/null
@@ -0,0 +1,1611 @@
+<?php //$Id$
+
+/**
+ * Library of functions and constants for blog
+ */
+require_once($CFG->dirroot .'/blog/class.BlogInfo.php');
+require_once($CFG->dirroot .'/blog/class.BlogEntry.php');
+require_once($CFG->dirroot .'/blog/class.BlogFilter.php');
+require_once($CFG->libdir .'/blocklib.php');
+require_once($CFG->libdir .'/pagelib.php');
+require_once($CFG->dirroot .'/blog/blogpage.php');
+
+/**
+ * Definition of blogcourse page type (blog page with course id present).
+ */
+//not used at the moment, and may not need to be
+define('PAGE_BLOG_COURSE_VIEW', 'blog_course-view');
+
+$BLOG_YES_NO_MODES = array ( '0'  => get_string('no'),
+                             '1' => get_string('yes') );
+
+//set default setting for $CFG->blog_* vars used by blog's blocks
+//if they are not already. Otherwise errors are thrown
+//when an attempt is made to use an empty var.
+if (empty($SESSION->blog_editing_enabled)) {
+    $SESSION->blog_editing_enabled = false;
+}
+if (!(isset($CFG->blog_enable_trackback_in) )) {
+    $CFG->blog_enable_trackback_in = 0; //default is 0 == do not allow for site
+}
+if (!(isset($CFG->blog_enable_moderation) )) {
+    $CFG->blog_enable_moderation = 0; //default is 0 == do not enable blog moderation on this site
+}
+if (!(isset($CFG->blog_enable_pingback_in) )) {
+    $CFG->blog_enable_pingback_in = 0; //default is 0 == do not allow for site
+}
+if (!(isset($CFG->blog_enable_trackback_out) )) {
+    $CFG->blog_enable_trackback_out = 0; //default is 0 == do not allow for site
+}
+if (!(isset($CFG->blog_enable_pingback_out) )) {
+    $CFG->blog_enable_pingback_out = 0; //default is 0 == do not allow for site
+}
+if (!(isset($CFG->blog_enable_moderation) )) {
+    $CFG->blog_enable_moderation = 0; //default is 0 == do not turn on moderation for site
+}
+if (!(isset($CFG->blog_useweblog_rpc) )) {
+    $CFG->blog_useweblog_rpc = 0;//default is 0 == do not publish to weblogs.com
+}
+if (empty($CFG->blog_ratename) ) {
+    $CFG->blog_ratename = 'Rating'; //default name for entry ratings
+}
+if (empty($CFG->blog_default_title) ) {
+    $CFG->blog_default_title = 'Moodle Blog'; //default blog title
+}
+if (empty($CFG->blog_blogurl) ) {
+    $CFG->blog_blogurl = $CFG->wwwroot.'/blog/index.php';
+}
+if (!(isset($CFG->blog_enable_trackback) )) {
+    $CFG->blog_enable_trackback = 0;
+}
+if (!(isset($CFG->blog_enable_pingback) )) {
+    $CFG->blog_enable_pingback = 0;
+}
+if (empty($CFG->blog_default_fetch_num_entries) ) {
+    $CFG->blog_default_fetch_num_entries = 8;
+}
+
+/**
+ * blog_user_bloginfo
+ *
+ * returns a blogInfo object if the user has a blog in the acl table
+ * This function stores the currently logged in user's bloginfo object
+ * statically - do not release/unset the returned object.
+ * added by Daryl Hawes for moodle integration
+ * $userid - if no userid specified it will attempt to use the logged in user's id
+ */
+function blog_user_bloginfo($userid='') {
+//Daryl Hawes note: not sure that this attempt at optimization is correct
+//    static $bloginfosingleton; //store the logged in user's bloginfo in a static var
+
+    if ($userid == '') {
+        global $USER;
+        if (!isset($USER) || !isset($USER->id)) {
+            return;
+        }
+        $userid = $USER->id;
+    }
+    
+/*    if (isset($USER) && $USER->id == $uid && !empty($bloginfosingleton)) {
+        return $bloginfosingleton;
+    }*/
+
+    $thisbloginfo = new BlogInfo($userid);
+/*        if (isset($USER) && $USER->id == $userid) {
+            $bloginfosingleton = $thisbloginfo;
+        }*/
+    return $thisbloginfo;
+}
+
+/**
+ * Verify that a user is logged in based on the session
+ * @return bool True if user has a valid login session
+ */
+function blog_isLoggedIn() {
+    global $USER;
+    if (!isguest() && isset($USER) and isset($USER->id) and $USER->id) {
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * This function upgrades the blog's tables as needed.
+ * It's called from moodle/admin/index.php
+ */
+function blog_upgrade_blog_db($continueto) {
+
+    global $CFG, $db;
+
+    require_once($CFG->dirroot.'/blog/version.php');  // Get code versions
+
+    if (empty($CFG->blog_version)) { // Blog has never been installed.
+        $strdatabaseupgrades = get_string('databaseupgrades');
+        print_header($strdatabaseupgrades, $strdatabaseupgrades, $strdatabaseupgrades,
+                     '', '', false, '&nbsp;', '&nbsp;');
+
+        $db->debug=true;
+        if (modify_database($CFG->dirroot .'/blog/db/'. $CFG->dbtype .'.sql')) {
+            $db->debug = false;
+            if (set_config('blog_version', $blog_version)) {
+                notify(get_string('databasesuccess'), 'green');
+                notify(get_string('databaseupgradeblog', 'blog', $blog_version));
+
+                /// Added by Daryl Hawes - since blog has to be installed the first time before blog's blocks, and on first install creating default blocks when none exist is a bad idea, just reset blocks for blog index here
+                require_once($CFG->libdir .'/pagelib.php');
+                require_once($CFG->dirroot .'/blog/blogpage.php');                
+                page_map_class(PAGE_BLOG_VIEW, 'page_blog');    
+                // Now, create our page object.
+                $page = page_create_object(PAGE_BLOG_VIEW, 0);
+                // Add default blocks to the new page type
+                blocks_repopulate_page($page);
+                
+                print_continue($continueto);
+                exit;
+            } else {
+                error('Upgrade of blogging system failed! (Could not update version in config table)');
+            }
+        } else {
+            error('blog tables could NOT be set up successfully!');
+        }
+    }
+
+
+    if ($blog_version > $CFG->blog_version) {       // Upgrade tables
+        $strdatabaseupgrades = get_string('databaseupgrades');
+        print_header($strdatabaseupgrades, $strdatabaseupgrades, $strdatabaseupgrades);
+
+        require_once($CFG->dirroot. '/blog/db/'. $CFG->dbtype .'.php');
+
+        $db->debug=true;
+        if (blog_upgrade($CFG->blog_version)) {
+            $db->debug=false;
+            if (set_config('blog_version', $blog_version)) {
+                notify(get_string('databasesuccess'), 'green');
+                notify(get_string('databaseupgradeblocks', '', $blog_version));
+                print_continue($continueto);
+                exit;
+            } else {
+                error('Upgrade of blogging system failed! (Could not update version in config table)');
+            }
+        } else {
+            $db->debug=false;
+            error('Upgrade failed!  See blog/version.php');
+        }
+
+    } else if ($blog_version < $CFG->blog_version) {
+        notify('WARNING!!!  The blog code you are using is OLDER than the version that made these databases! ('. $version .' < '. $CFG->blog_version .')');
+    }
+}
+
+/**
+ * course_has_blog_entries
+ *   Given a course id return true if there are blog entries from any user related to that course
+ * $courseid - the id for the course
+ * Daryl Hawes note: When forum entries start using post table this function will no longer behave as expected
+ * Since non blog posts will be associated with this course. Perhaps moduleid or another field could be wrangled 
+ * into identifying the type of post?
+ */
+function blog_course_has_blog_entries($courseid) {
+    $entries = get_records('post', 'courseid', $courseid);
+    if (! empty($entries) ) {
+        return true;
+    }
+    return false;
+}
+
+/**
+ * Output a hidden html form used by text entry pages that require preview.php's functionality
+ */
+function blog_print_preview_form($userid=0, $categoryelement='<input type="hidden" name="categoryid[]">', $return=false) {
+
+    $returnstring = "\n".'<div id="prev" style="visibility:hidden;z-index:-1;position:absolute;display:none;">';
+    $returnstring .= "\n".'<form name="prev" action="preview.php" method="post" target="preview">';
+    $returnstring .= "\n".'<input type="hidden" name="etitle" />';
+    $returnstring .= "\n".'<input type="hidden" name="body" />';
+    $returnstring .= "\n".'<input type="hidden" name="extendedbody" />';
+    $returnstring .= "\n".'<input type="hidden" name="comm" />';
+    $returnstring .= "\n".'<input type="hidden" name="tem" />';
+    $returnstring .= "\n".'<input type="hidden" name="userid" value="'. $userid .'" />';
+
+//    $returnstring .= '<input type="hidden" name="categoryid[]" value="'. $categoryid .'" />';
+    $returnstring .= "\n". $categoryelement;
+    $returnstring .= "\n".'<input type="hidden" name="format" />';
+    $returnstring .= "\n".'</form>';
+    $returnstring .= "\n".'</div>'."\n";
+    
+    if ($return) {
+        return $returnstring; //return the form as a string if requested
+    }
+    print $returnstring; //else just print the string and exit the function
+}
+
+/**
+ * blog_user_has_rights - returns true if user is the blog's owner or a moodle admin.
+ *
+ * @param BlogInfo blogInfo - a BlogInfo object passed by reference. This object represents the blog being accessed.
+ * @param int uid - numeric user id of the user whose rights are being tested against this blogInfo. If no uid is specified then the uid of the currently logged in user will be used.
+ */
+function blog_user_has_rights(&$bloginfo, $uid='') {
+    global $USER;
+    if (isset($bloginfo) && isset($bloginfo->userid)) {
+        if ($uid == '') {
+            if ( isset($USER) && isset($USER->id) ) {
+                $uid = $USER->id;
+            }
+        }        
+        if ($uid == '') {
+            //if uid is still empty then the user is not logged in
+            return false;
+        }
+        if (blog_is_blog_admin($uid) || isadmin()) {
+            return true;
+        } 
+    }
+    return false;
+}
+  
+/**
+ * Determines whether a user is an admin for a blog
+ * @param int $blog_userid The id of the blog being checked
+ */
+function blog_is_blog_admin($blog_userid) {
+    global $USER;
+
+    //moodle admins are admins
+    if (isadmin()) {
+        return true;
+    }
+    if ( empty($USER) || !isset($USER->id) ) {
+        return false;
+    }
+    if ( empty($blog_userid)) {
+        return true;
+    }
+
+    // Return true if the user is an admin for this blog
+    if ($blog_userid == $USER->id) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+/**
+ * Adaptation of isediting in moodlelib.php for blog module
+ * @return bool
+ */
+function blog_isediting() {
+    global $SESSION;
+    if (! isset($SESSION->blog_editing_enabled)) {
+        $SESSION->blog_editing_enabled = false;
+    }
+    return ($SESSION->blog_editing_enabled);
+}
+
+/**
+ * Turns a php string into a string ready for insert into an rss xml feed
+ */
+function blog_rss_content($str) {
+    $str = superhtmlentities($str);
+    $content = "<![CDATA[$str]]>\n";
+
+    return $content;
+}
+
+/**
+ * function found when searching on problem of smart quotes
+ * posted at http://daynah.php-princess.net/index.php?p=94
+ */
+function superhtmlentities($text) {
+  $entities = array(128 => 'euro', 130 => 'sbquo', 
+  131 => 'fnof', 132 => 'bdquo', 133 => 'hellip', 
+  134 => 'dagger', 135 => 'Dagger', 136 => 'circ', 
+  137 => 'permil', 138 => 'Scaron', 139 => 'lsaquo', 
+  140 => 'OElig', 145 => 'lsquo', 146 => 'rsquo', 
+  147 => 'ldquo', 148 => 'rdquo', 149 => 'bull', 
+  150 => 'ndash', 151 => 'mdash', 152 => 'tilde', 
+  153 => 'trade', 154 => 'scaron', 155 => 'rsaquo', 
+  156 => 'oelig', 159 => 'Yuml');
+
+  $new_text = '';
+  for($i = 0; $i < strlen($text); $i++) {
+    $num = ord($text{$i});
+    if (array_key_exists($num, $entities)) {
+        $new_text .= '&\\'.$entities[$num].';';
+    } else if ($num < 127 || $num > 159) {
+        $new_text .= $text{$i};
+    }
+  }
+  return htmlentities($new_text);
+}
+
+/**
+ * blog_get_recent_entries_byrange
+ * not in blogInfo because entries being searched 
+ * can be found in any number of blogs rather than just one.
+ *
+ * Returns specified range of entries since a given time.
+ *
+ * In using this function be aware for your where clause that the tables being 
+ * read are post (e) and blog_categories_entries (c) and the returned values
+ * will be e.*
+ *
+ * @param int $limit .
+ * @param int $start .
+ * @param string $where .
+ * @param string $orderby .
+ * @param bool $includeCategories .
+ *
+ * @return BlogEntries 
+ */
+function blog_get_recent_entries_byrange($limit, $start, $where='', $orderby='lastmodified DESC', $includeCategories=false) {
+    global $CFG;
+
+//    echo 'Debug: where clause in blog_get_recent_entries_byrange: $where<br />'; //debug
+
+    if ($includeCategories) {
+        $records = get_records_select('post e, '. $CFG->prefix .'blog_categories_entries c', $where, $orderby, '*', $start, $limit);
+    } else {
+        $records = get_records_select('post', $where, $orderby, '*', $start, $limit);
+    }
+//    print_object($records); //debug
+
+    if (empty($records)) {
+        return array();
+    } else {
+        $blogEntries = array();
+        foreach($records as $record) {
+            $blogEntry = new BlogEntry($record);
+            //ensure that the user has rights to view this entry
+            if ($blogEntry->user_can_view() ) {
+                $blogEntries[] = $blogEntry;
+            }
+        }
+        return $blogEntries;
+    }
+}
+
+/**
+ * returns a unix time stamp from year month and day
+ * used to get start and end dates for comparing against timestamps in databases
+ */
+function blog_get_month_time($y, $m, $d, $firstday=true) {
+
+    if ( !empty($y) && !empty($m) && !empty($d)) {
+        $time = mktime(0, 0, 0, $m, $d, $y);
+    } else if ( !empty($y) && !empty($m) ) {
+        $day = blog_mk_getLastDayofMonth($m, $y);
+        if ($firstday) {
+            $day = 1;
+        }
+        $time = mktime(0, 0, 0, $m, $day, $y);
+    } else {
+        $time = '';
+    }
+    return $time;
+}
+
+/**
+ * attempting to create a method useful for a popup form on the index
+ * page that will allow a user to filter entries based upon date
+ *
+ * This function will return a list, in order, formatted as "MM-YYYY" for each year and month
+ * in which this blog has entries.
+ * @param BlogFilter $blogFilter - a BlogFilter object with the current filter settings for this page
+ */
+function blog_get_year_month_of_viewable_entries(&$blogFilter) {
+    global $_SERVER;
+    
+    $entries = $blogFilter->get_filtered_entries();
+    $datearray = array();
+    $datearray['m=&amp;y='] = 'All Dates';
+    
+    if ( !empty($entries) ) {
+        foreach ($entries as $entry) {
+            if ( $entry->user_can_view() ) {
+//                print_object($entry); //debug
+                //user is allowed to see this entry, so return its year/month information
+                $date = $entry->entryLastModified;
+                $truncDate = date("F Y", $date); // this will return January 2004 for the date.
+                $curmonth = date("m", $date); // this will return January 2004 for the date.
+                $curyear = date("Y", $date); // this will return January 2004 for the date.
+                $index = 'm='. $curmonth .'&amp;y='. $curyear;
+                $datearray[$index] = $truncDate;
+            }
+        }
+    }
+//    print_object($datearray); //debug
+        
+    if (is_numeric($blogFilter->startmonth) && is_numeric($blogFilter->startyear)) {
+        $selected = 'm='. $blogFilter->startmonth .'&amp;y='. $blogFilter->startyear;
+    } else {
+        $selected = '';
+    }
+    $unused = array('startmonth', 'startyear');
+    $getvars = $blogFilter->get_getvars($unused);
+    //attach a random number to the popup function's form name
+    //becuase there may be multiple instances of this form on each page
+    $self = basename($_SERVER['PHP_SELF']); 
+    $form = popup_form($self . $getvars .'&amp;', $datearray, 'blog_date_selection'. mt_rand(), $selected, '', '', '', true);
+    return str_replace('<form', '<form style="display: inline;"', $form);
+}
+
+/**
+ * pretty close to a direct copy of calendar/view.php calendar_course_filter_selector function
+ */
+function blog_course_filter_selector(&$blogFilter) {
+    global $USER, $_SERVER;
+    
+    $getvars = $blogFilter->get_getvars('courseid');
+
+    if ( !isset($USER) || !isset($USER->id) ) {
+        return;
+    }
+
+    if (isguest($USER->id)) {
+        return '';
+    }
+    
+    if (isadmin($USER->id)) {
+        $courses = get_courses('all', 'c.shortname');
+        
+    } else {
+        $courses = get_my_courses($USER->id, 'shortname');
+    }
+    
+    unset($courses[1]);
+    
+    $courseoptions[1] = get_string('fulllistofcourses');
+    foreach ($courses as $course) {
+        // Verify that there are actually blog entries for this course before showing it as a selection.
+        if ($entries = count_records('post', 'courseid', $course->id)) {
+            $courseoptions[$course->id] = $course->shortname;
+        }
+    }
+
+    //if there were no courses added then simply return
+    if (count($courseoptions) == 1) {
+        return;
+    }
+    
+    if (is_numeric($blogFilter->courseid)) {
+        $selected = $blogFilter->courseid;
+    } else {
+        $selected = '';
+    }
+    //attach a random number to the popup function's form name
+    //because there may be multiple instances of this form on each page
+    $self = basename($_SERVER['PHP_SELF']); 
+    $form = popup_form($self . $getvars .'&amp;courseid=',
+                       $courseoptions, 'blog_course_selection'. mt_rand(), $selected, '', '', '', true);
+    
+    return str_replace('<form', '<form style="display: inline;"', $form);
+}
+
+/**
+ * build and return list of all member blogs
+ *
+ * @param stdObject $memberrecords An object of record entries as output from the get_member_list() function in BlogFilter (->id, ->title are the required variables).
+ * @param int $format - 0, 1 or 2; 0 = hyperlinked list of members, 1 = select element, 2 = select element wrapped in entire form
+ * @param bool $return  indicates whether the function should return the text 
+ *   as a string or echo it directly to the page being rendered
+ * @param BlogFilter $blogFilter - a BlogFilter object with the details of the members listed in $memberrecords.
+ * @param string $hyperlink This is the target link to be used - there is a sensible default for each format.
+ */
+function blog_member_list(&$blogFilter, &$memberrecords, $format=1, $return=true, $hyperlink='') {
+    global $CFG, $USER;
+    
+//echo "userid = $blogFilter->userid"; //debug
+//print_object($memberrecords); //debug
+    $returnstring = '';
+    if (!empty($memberrecords)) {
+        switch($format) {
+            case '0':
+                foreach($memberrecords as $record) {
+                    if (empty($hyperlink)) {
+                        $CFG->wwwroot .'/blog/index.php?userid='. $record->id; 
+                    }
+                    $returnstring .= '<a href="'. $hyperlink . $record->id .'">'. stripslashes_safe($record->title) .'</a><br />';     
+                }
+                break;
+            case '2':
+                $selected = '';
+                $options = array('' => 'All Member Blogs');
+                $formlink = $hyperlink; //TESTING
+                if (empty($hyperlink)) {
+                    $getvars = $blogFilter->get_getvars('userid');
+                    $self = basename($_SERVER['PHP_SELF']); 
+                    $formlink = $self . $getvars .'&amp;userid=';
+                }                    
+                foreach($memberrecords as $record) {
+                    $id = $record->id;
+                    if (blog_isLoggedIn() && $id == $USER->id ) {
+                        $optiontitle = 'My Blog';
+                    } else {
+                        $optiontitle = stripslashes_safe($record->title);
+                    }
+                    $options[$id] = $optiontitle; //TESTING
+                    if ( ($blogFilter->userid == $record->id) && ($blogFilter->userid != 0) ) {
+                        $selected = $id;
+                    }
+                }
+                
+                //attach a random number to the popup function's form name
+                //becuase there may be multiple instances of this form on each page
+                $returnstring = popup_form($formlink,
+                       $options, 'blog_member_list'. mt_rand(), $selected, '', '', '', true);
+                $returnstring = str_replace('<form', '<form style="display: inline;"', $returnstring);
+                break;
+            
+            case '1':
+            default:
+                $returnstring = '<select name="userid">';
+                foreach($memberrecords as $record) {            
+                    $returnstring .= '<option value="'. $record->id .'"';
+                    if ( ($record->id == $blogFilter->userid) && ($blogFilter->userid != 0) ) {
+                        $returnstring .= ' selected';
+                    }
+                    $returnstring .= '>'. stripslashes_safe($record->title) .'</option>';
+                }
+                $returnstring .= '</select>';
+                break;
+        }
+    
+    }
+    if ($return) {
+        return $returnstring;
+    }
+    print $returnstring;
+    return;
+}
+
+/**
+ * @param int courseid the selected course in popup
+ */
+function blog_get_course_selection_popup($courseid='') {
+    global $USER;
+    if ( !isset($USER) || !isset($USER->id) ) {
+        return;
+    }
+    if ( isadmin() ) {
+        $courses = get_courses(); //show admin users all courses
+    } else {
+        $courses = get_my_courses($USER->id) ; //get_my_courses is in datalib.php
+    }
+    //print_object($courses); //debug
+    $courseoptions = array();
+    foreach ($courses as $course) {
+        $courseoptions[$course->id] = $course->shortname;
+    }
+    return choose_from_menu($courseoptions, 'course_selection', $courseid, '', '', '0', true);
+}
+
+/**
+ *  This function is in lib and not in BlogInfo because entries being searched
+ *   might be found in any number of blogs rather than just one.
+ *
+ *   $@param BlogFilter blogFilter - a BlogFilter object containing the settings for finding appropriate entries for display
+ */
+function blog_print_html_formatted_entries(&$blogFilter, $filtertype, $filterselect) {
+    global $CFG, $USER;
+    $blogpage = optional_param('blogpage', 0);
+    $bloglimit = get_user_preferences('blogpagesize',8); // expose as user pref when MyMoodle comes around
+
+    // First let's see if the batchpublish form has submitted data
+    $post = data_submitted();
+    if (!empty($post->batchpublish)) { //make sure we're processing the edit form here
+//        print_object($post); //debug
+        foreach ($post as $key => $publishto) {
+            if ($key != 'batchpublish') {
+                $useridandentryid = explode('-', $key);
+                $userid = $useridandentryid[0];
+                $entryid = $useridandentryid[1];
+                $bloginfo = new BlogInfo($userid);
+                $blogentry = $bloginfo->get_blog_entry_by_id($entryid);
+                if ($blogentry->entryPublishState != $publishto) {
+                    if (!$blogentry->set_publishstate($publishto)) {
+                        echo 'Entry "'. $blogentry->entryTitle .'" could not be published.';
+                    } else {
+                        if ($error = $blogentry->save()) {
+                            echo 'New publish setting for entry "'. $blogentry->entryTitle .'" could not be saved. ERROR:'. $error.':<br />';
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    $morelink = '<br />&nbsp;&nbsp;';
+    // show personal or general heading block as applicable
+    echo '<div class="headingblock header blog">';
+    //show blog title - blog tagline
+    print "<br />";    //don't print title. blog_get_title_text();
+
+    if ($blogpage != 0) {
+        // modify the blog filter to fetch the entries we care about right now
+        $oldstart = $blogFilter->fetchstart;
+        $blogFilter->fetchstart = $blogpage * $bloglimit;
+        unset($blogFilter->filtered_entries);
+    }
+    $blogEntries = $blogFilter->get_filtered_entries();
+    // show page next/previous links if applicable
+    print_paging_bar($blogFilter->get_viewable_entry_count(), $blogpage, $bloglimit, $blogFilter->baseurl, 'blogpage');
+    print '</div>';
+    if (isset($blogEntries) ) {
+
+        if (blog_isLoggedIn() && blog_isediting() ) {
+            print '<form name="batchpublishform" method="post" action="'. $blogFilter->baseurl .'" id="batchpublishform" enctype="multipart/form-data">';
+        }
+
+        $count = 0;
+        foreach ($blogEntries as $blogEntry) {
+            blog_print_entry($blogEntry, 'list', $filtertype, $filterselect); //print this entry.
+            $count++;
+        }
+        if (!$count) {
+            print '<br /><center>'. get_string('noentriesyet', 'blog') .'</center><br />';
+
+            if (blog_isLoggedIn()) {
+                $morelink = '<br />&nbsp;&nbsp;';
+                $morelink .= $blogFilter->get_complete_link('<a href="'. $CFG->wwwroot .'/blog/edit.php', get_string('addentries', 'blog'))."\n";
+                
+            }
+        }
+/*
+        if (blog_isLoggedIn() && blog_isediting() ) {
+            //Daryl Hawes note: localize this submit button!
+            print '<div align="center"><input type="submit" value="Save these publish settings" id="batchpublish" name="batchpublish" /></div>'."\n";
+            print '</form>'."\n";
+        }
+*/
+        //yu: testing code
+        if (blog_isLoggedIn()) {
+        //the user's blog is enabled and they are viewing their own blog
+            $morelink .= $blogFilter->get_complete_link($CFG->wwwroot .'/blog/edit.php', get_string('addentries', 'blog'));
+        }
+
+        print $morelink.'<br />'."\n";
+
+        if ($blogpage != 0) {
+            //put the blogFilter back the way we found it
+            $blogFilter->fetchstart = $oldstart;
+            unset($blogFilter->filtered_entries);
+            $blogFilter->fetch_entries();
+        }
+
+        return;
+    }
+
+    $output = '<br /><center>'. get_string('noentriesyet', 'blog') .'</center><br />';
+    $userbloginfo = blog_user_bloginfo();
+    
+    if (blog_isLoggedIn()) {
+        //the user's blog is enabled and they are viewing their own blog
+        $output .= $blogFilter->get_complete_link($CFG->wwwroot .'/blog/edit.php', get_string('addentries', 'blog'));
+    }
+    print $output;
+    unset($blogFilter->filtered_entries);
+}
+
+/**
+ * What text should be displayed claiming ownership to the current blog entries?
+ * @uses $PAGE
+ * @return string
+ */
+function blog_get_title_text() {
+    global $PAGE; //hackish
+
+    if (isset($PAGE) && isset($PAGE->bloginfo)) {
+        $blogInfo = &$PAGE->bloginfo;
+        $title = $blogInfo->get_blog_title();
+        if($title != '') {
+            $displaytitle = $title;
+            $tagline = $blogInfo->get_blog_tagline();
+            if ($tagline != '') {
+                $displaytitle .= ' - '. $tagline;
+            }
+        }
+    }
+    if (isset($displaytitle)) {
+        return $displaytitle;
+    } else {
+        // Daryl Hawes - better wording would be good here, localize this line once the wording is selected.
+        return 'Combined Blog '. get_string('entries', 'blog') .'<br />';
+    }
+}
+
+/**
+ * blog_get_moodle_pix_path
+ *
+ * Returns the directory path to the current theme's pix folder.
+ * @return string
+ */
+function blog_get_moodle_pix_path(){
+    global $CFG, $THEME;
+    if (empty($THEME->custompix)) {
+        return $CFG->wwwroot.'/pix';
+    } else {
+        return $CFG->themedir.current_theme().'/pix';
+    }
+}
+
+/**
+ *  This function is in lib and not in BlogInfo because entries being searched
+ *   might be found in any number of blogs rather than just one.
+ *
+ * This function builds an array which can be used by the included
+ * template file, making predefined and nicely formatted variables available
+ * to the template. Template creators will not need to become intimate
+ * with the internal objects and vars of moodle blog nor will they need to worry
+ * about properly formatting their data
+ *
+ *   @param BlogEntry blogEntry - a hopefully fully populated BlogEntry object
+ *   @param string viewtype Default is 'full'. If 'full' then display this blog entry
+ *     in its complete form (eg. archive page). If anything other than 'full'
+ *     display the entry in its abbreviated format (eg. index page)
+ */
+function blog_print_entry(&$blogEntry, $viewtype='full', $filtertype, $filterselect) {
+    global $CFG, $THEME, $USER;
+    static $bloginfoarray;
+
+    if (isset($bloginfoarray) && $bloginfocache[$blogEntry->entryuserid]) {
+        $bloginfo = $bloginfocache[$blogEntry->entryuserid];
+    } else {
+        $bloginfocache[$blogEntry->entryuserid] = new BlogInfo($blogEntry->entryuserid);
+        $bloginfo = $bloginfocache[$blogEntry->entryuserid];
+    }
+
+    $template['blogtitle'] = $bloginfo->blogtitle;
+    $template['blogtagline'] = $bloginfo->blogtagline;
+    $template['blogurl'] = $bloginfo->get_blogurl();
+    
+    $template['body'] = $blogEntry->get_formatted_entry_body();
+    $template['countofextendedbody'] = 0;
+    
+    if ($template['extendedbody'] = $blogEntry->get_formatted_entry_extended_body()) {
+        $template['extendedbody'] = $blogEntry->get_formatted_entry_extended_body();
+        $template['countofextendedbody'] = count_words($template->extendedbody);
+    } else {
+        $template['extendedbody'] = '';
+    }
+    
+    if ($viewtype=='full' && !empty($template['extendedbody'])) {
+        $template['body'] .= '<hr width="80%" />' . $template['extendedbody'];
+    } else if ( !empty($template->extendedbody) && $template->countofextendedbody != 0) {
+        $template['body'] .= '<br />&nbsp;&nbsp;&nbsp;<a href="'. $blogEntry->get_entryurl() .'">'. get_string('moreelipses', 'blog') .' ('. $template['countofextendedbody'] .' words)</a>';
+    }
+
+    $template['title'] = '<a name="'. $blogEntry->entryId .'"></a>';
+    //enclose the title in nolink tags so that moodle formatting doesn't autolink the text
+    $template['title'] .= '<span class="nolink">'. stripslashes_safe($blogEntry->entryTitle);
+    $template['title'] .= '</span>';
+
+    // add editing controls if allowed
+    $template['editbuttons'] = $blogEntry->get_formatted_edit_URL(true);
+    $template['editbuttons'] .= $blogEntry->get_formatted_delete_URL(true);
+    $template['courseid'] = $blogEntry->entryCourseId;
+    $template['userid'] = $blogEntry->entryuserid;
+    $template['authorviewurl'] = $CFG->wwwroot .'/user/view.php?course=1&amp;id='. $template['userid'];
+    $template['moodlepix'] = blog_get_moodle_pix_path();
+    $template['author'] = $blogEntry->entryAuthorName;
+    $template['lastmod'] = $blogEntry->formattedEntryLastModified;
+    $template['created'] = $blogEntry->formattedEntryCreated;
+    $template['publishtomenu'] = $blogEntry->get_publish_to_menu(true, true);
+    $template['groupid'] = $blogEntry->entryGroupId;
+    //forum style printing of blogs
+    blog_print_entry_content ($template, $blogEntry->entryId, $filtertype, $filterselect);
+
+}
+
+//forum style printing of blogs
+function blog_print_entry_content ($template, $entryid, $filtertype='', $filterselect='') {
+    global $USER, $CFG, $course, $ME;
+
+    $stredit = get_string('edit');
+    $strdelete = get_string('delete');
+
+    $user = get_record('user','id',$template['userid']);
+
+    echo '<div align="center"><table cellspacing="0" class="forumpost" width="100%">';
+
+    echo '<tr class="header"><td class="picture left">';
+    print_user_picture($template['userid'], $template['courseid'], $user->picture);
+    echo '</td>';
+
+    echo '<td class="topic starter"><div class="subject">'.$template['title'].'</div><div class="author">';
+    $fullname = fullname($user, isteacher($template['userid']));
+    $by->name =  '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
+                $user->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
+    $by->date = $template['lastmod'];
+    print_string('bynameondate', 'forum', $by);
+    echo '</div></td></tr>';
+
+    echo '<tr><td class="left side">';
+    if ($group = get_record('groups','id',$template['groupid'])) {
+        print_group_picture($group, $course->id, false, false, true);
+    } else {
+        echo '&nbsp;';
+    }
+
+/// Actual content
+
+    echo '</td><td class="content">'."\n";
+
+    // Print whole message
+    echo format_text($template['body']);
+
+/// Links to tags
+
+    if ($blogtags = get_records_sql('SELECT tags.* FROM '.$CFG->prefix.'tags, '.$CFG->prefix.'blog_tag_instance
+                                 WHERE tags.id = blog_tag_instance.tagid
+                                 AND blog_tag_instance.entryid = '.$entryid)) {
+        echo '<p />';
+        print_string('tags', 'blog');
+        foreach ($blogtags as $blogtag) {
+            echo '<a href="index.php?courseid='.$course->id.'&amp;filtertype='.$filtertype.'&amp;filterselect='.$filterselect.'&amp;tagid='.$blogtag->id.'">'.$blogtag->text.'</a>, ';
+        }
+    }
+    
+/// Commands
+
+    echo '<div class="commands">';
+
+    if (isset($USER->id)) {
+        if (($template['userid'] == $USER->id) or isteacher($course->id)) {
+                echo '<a href="'.$CFG->wwwroot.'/blog/edit.php?editid='.$entryid.'">'.$stredit.'</a>';
+        }
+
+        if (($template['userid'] == $USER->id) or isteacher($course->id)) {
+            echo '| <a href="'.$CFG->wwwroot.'/blog/edit.php?act=del&amp;postid='.$entryid.'">'.$strdelete.'</a>';
+        }
+    }
+
+    echo '</div>';
+
+    echo '</td></tr></table></div>'."\n\n";
+}
+
+/**
+ * Use this function to retrieve a list of publish states available for 
+ * the currently logged in user.
+ *
+ * @return array This function returns an array ideal for sending to moodles'
+ *                choose_from_menu function.
+ */
+function blog_applicable_publish_states($courseid='') {
+    global $CFG;
+    
+    // everyone gets draft access
+    $options = array ( 'draft' => get_string('publishtonoone', 'blog') );
+    if (is_numeric($courseid) && $courseid != SITEID && $course = get_record('course', 'id', $courseid, '', '', '', '', 'shortname') ) {
+        require_login($courseid);
+        // if we're viewing a course allow publishing to course teachers
+        $options['teacher'] = get_string('publishtoteachers', 'blog', $course->shortname);
+        if (!$CFG->blog_enable_moderation || isadmin() || isteacher($courseid) ) {
+            // only admins and teachers can publish to course members when moderation is enabled
+            $options['course'] = get_string('publishtocourse', 'blog', $course->shortname);
+        }
+    }
+    /*
+     //groups not supported quite yet - pseudocode:
+     if (isset($post->groupid) && $post->groupid != '') {
+         $options['group'] = 'Fellow group members and teachers can view';
+     }*/
+    if (!$CFG->blog_enable_moderation || isadmin() || (is_numeric($courseid) && isteacher($courseid)) ) {
+        // only admins and teachers can see site and public options when moderation is enabled
+        $options['site'] = get_string('publishtosite', 'blog');
+        $options['public'] = get_string('publishtoworld', 'blog');
+    }
+    return $options;
+}
+
+/**
+ * blog_get_entries_by_category - not in blogInfo because entries being searched 
+ *   can be found in any number of blogs rather than just one.
+ * catids can be an array of category ids or a single category id
+ * defined as either
+ * $catids = array(1, 2, 3, 4, 5);
+ * or
+ * $catids = 2;
+ *
+ * Used by rss.php
+ */
+function blog_get_entries_by_category($catids, $courseid=0, $limit=8, $start=0) {
+    $catsearch = ' e.id=c.entryid AND '; //only gets categories whose postid matches entries retrieved
+    if (blog_array_count($catids) > 0) {
+        $count = 0;
+        foreach ($catids as $catid) {
+            $catsearch .= 'c.categoryid='. $catid .' ';
+            $count += 1;
+            if (count($catids) != $count) {
+                $catsearch .= 'OR ';
+            }
+        } 
+    } else {
+        $catsearch .= 'c.categoryid='. $catids;
+    }
+    $wherecourse = '';
+    if (is_numeric($courseid) && $courseid != 0 && $courseid != 1) {
+        $wherecourse = ' AND e.courseid='. $courseid;
+    }
+    $where = $catsearch.$wherecourse;
+//    echo 'Debug: where clause for blog_get_entries_by_category: '. $where; //debug
+    return blog_get_recent_entries_byrange($limit, $start, $where, '', true);
+}
+
+/**
+ * builds calendar with links to filter entries by date
+ * modified by Daryl Hawes to build links even when no userid is specified
+ * and to return the html as a string if desired
+ */
+function blog_draw_calendar(&$blogFilter, $return=false)
+{
+    global $CFG;
+//    print_object($blogFilter);
+
+    if (!empty($blogFilter->startmonth)) {
+        $m = $blogFilter->startmonth;
+    } else {
+        $m = date('n', mktime());
+        $blogFilter->startmonth = $m;
+    }
+    if (!empty($blogFilter->startyear)) {
+        $y = $blogFilter->startyear;
+    } else {
+        $y = date('Y', mktime());
+        $blogFilter->startyear = $y;
+    }
+    $userid = $blogFilter->userid;
+    
+    //create a string to represent a URL argument with userid info. If no userid then string is empty.
+    $useridString = '&amp;userid='. $userid;
+    if ($userid == 0 || $userid == '') {
+        $useridString = '';
+    }
+
+   // calculate the weekday the first of the month is on
+   $tmpd = getdate(mktime(0, 0, 0, $m, 1, $y));
+   $monthname = $tmpd['month'];
+   $firstwday= $tmpd['wday'];
+   $today = date('Ymd', mktime());
+
+   $lastday = blog_mk_getLastDayofMonth($m, $y);
+
+   //determine next and previous month
+   if (($m - 1) < 1) { $pm = 12; } else { $pm = $m - 1; }
+   if (($m + 1) > 12) { $nm = 1; } else { $nm = $m + 1; }
+
+   if (strlen($pm) == 1) { $pm = '0'. $pm; };
+   if (strlen($nm) == 1) { $nm = '0'. $nm; };
+
+   $returnstring = "\n".'<table class="generaltable"><tr>'."\n";
+   $returnstring .= '<td style="text-align: left; width: 12%;">'."\n";
+
+   $currentyear = $y;
+   $currentmonth = $m;
+
+   if (($m - 1) < 1) {
+       $blogFilter->startyear = $y - 1;
+   } else {
+       $blogFilter->startyear = $y;
+   }
+
+   $blogFilter->startmonth = $pm;
+   $self = basename($_SERVER['PHP_SELF']); 
+   $returnstring .= $blogFilter->get_complete_link( $self, '&lt;&lt;' , array('startday'))."\n";
+
+   $blogFilter->startyear = $currentyear;
+   $blogFilter->startmonth = $currentmonth;
+
+   $returnstring .= '</td><td style="text-align: center;">'."\n";
+//   $returnstring .= $blogFilter->get_complete_link($CFG->wwwroot .'/blog/archive.php', $monthname .' '. $y, array('startday'));
+   $returnstring .= $blogFilter->get_complete_link($self, $monthname .' '. $y, array('startday'));
+
+   if (($m + 1) > 12) {
+       $blogFilter->startyear = $blogFilter->startyear + 1;
+   } else {
+        $blogFilter->startyear = $y;
+   }
+
+   $blogFilter->startmonth = $nm;
+   $returnstring .= '</td><td style="text-align: right; width: 12%;">'."\n";
+   $returnstring .= $blogFilter->get_complete_link( $self, '&gt;&gt;', array('startday'))."\n";
+   $returnstring .= '</td></tr>'."\n";
+
+   $blogFilter->startyear = $currentyear;
+   $blogFilter->startmonth = $currentmonth;
+
+   $returnstring .= '<tr><td colspan="3">'."\n";
+   $returnstring .= '<table class="calendarmini"><thead>'."\n";
+   $returnstring .= '<tr><td width="19" align="center" class="calday">'. get_string('calsun', 'blog') .'</td>'."\n";
+   $returnstring .= '<td width="19" align="center" class="calday">'. get_string('calmon', 'blog') .'</td>'."\n";
+   $returnstring .= '<td width="19" align="center" class="calday">'. get_string('caltue', 'blog') .'</td>'."\n";
+   $returnstring .= '<td width="19" align="center" class="calday">'. get_string('calwed', 'blog') .'</td>'."\n";
+    $returnstring .= '<td width="19" align="center" class="calday">'. get_string('calthu', 'blog') .'</td>'."\n";
+    $returnstring .= '<td width="19" align="center" class="calday">'. get_string('calfri', 'blog') .'</td>'."\n";
+    $returnstring .= '<td width="19" align="center" class="calday">'. get_string('calsat', 'blog') .'</td></tr></thead><tbody>'."\n";
+
+    $d = 1;
+    $wday = $firstwday;
+    $firstweek = true;
+
+    // loop through all the days of the month
+    while ( $d <= $lastday)
+    {
+        // set up blank days for first week
+        if ($firstweek) {
+        $returnstring .= '<tr>'."\n";
+        for ($i=1; $i <= $firstwday; $i++) {
+            $returnstring .= '<td>&nbsp;</td>'."\n";
+        }
+        $firstweek = false;
+        }
+
+       // Sunday start week with <tr>
+       if ($wday==0) {
+           $returnstring .= '<tr>'."\n";
+        }
+
+       $mo = $m;
+       if ($mo < 10) {
+           if (!preg_match("/0\d/", $mo)) {
+                $mo = '0'. $mo;
+           }
+       }
+
+        // Look for blog entries for this day
+       $tstart = blog_get_month_time($y, $m, $d, true);
+       $tend = blog_get_month_time($y, $m, $d + 1, false);
+       $where = " lastmodified >= $tstart AND lastmodified <= $tend ";
+
+        if ($userid != 0 && $userid != '') {
+            $where .= ' AND author = '. $userid .' ';
+        }
+
+      $count = count_records_select('post', $where);
+
+//echo 'Where clause: '. $where .' | count:'. $count. '<br />'."\n"; //debug
+        $da = $d;
+        if($da < 10) {
+            if(!preg_match("/0\d/", $da)) {
+                $da = "0". $da;
+            }
+        }        
+      // check for event
+      $showdate = $y . $mo . $da;
+
+      $returnstring .= '<td align=center';
+      if ($showdate == $today) {
+           $returnstring .= ' class="cal_today"';
+      }
+      if ($wday == 6 || $wday == 0) { 
+          $returnstring .= ' class="cal_weekend"';
+      }
+      $returnstring .= '>'."\n";
+
+      // if entries are found, output link to that day's entries
+      if ($count > 0) {
+          $blogFilter->startday = $d;
+          $returnstring .= $blogFilter->get_complete_link($CFG->wwwroot .'/blog/index.php', $d);
+      } else {
+           $returnstring .= $d."\n";
+      }
+      $returnstring .= '</td>'."\n";
+
+      // Saturday end week with </tr>
+      if ($wday == 6) { 
+          $returnstring .= '</tr>'."\n"; 
+      }
+      $wday++;
+      $wday = $wday % 7;
+      $d++;
+    }
+
+    if ($wday != 0) {
+        for($i = $wday; $i < 7; $i++) {
+            $returnstring .= '<td>&nbsp;</td>'."\n";
+        }
+        $returnstring .= '</tr>'."\n";
+    }
+    
+    $returnstring .= '</table>'."\n";
+
+    $returnstring .= '</td></tr></tbody></table>'."\n";
+
+    if ($return) {
+        return $returnstring;
+    }
+    print $returnstring;
+// end blog_draw_calendar function
+}
+
+/**
+ * get the last day of the month
+ */
+function blog_mk_getLastDayofMonth($mon, $year)
+{
+    for ($tday=28; $tday <= 31; $tday++)
+    {
+        $tdate = getdate(mktime(0, 0, 0, $mon, $tday, $year));
+        if ($tdate['mon'] != $mon)
+            { break; }
+
+    }
+    $tday--;
+
+    return $tday;
+}
+
+/**
+ * blog_safeHTML
+ *   Clean up user input (currently unused, moodle's format_text() is preferable)
+ */
+function blog_safeHTML($html, $tags = 'b|br|i|u|ul|ol|li|p|a|blockquote|em|strong') {
+// removes all tags that are considered unsafe
+// Adapted from a function posted in the comments about the strip_tags()
+// function on the php.net web site.
+//
+// This function is not perfect! It can be bypassed!
+// Remove any nulls from the input
+    $html = preg_replace('/\0/', '', $html);
+    // convert the ampersands to null characters (to save for later)
+    $html = preg_replace('/&/', '\0', $html);
+  
+    // convert the sharp brackets to their html code and escape special characters such as "
+    $html=htmlspecialchars($html);
+   
+    // restore the tags that are considered safe
+    if ($tags) {
+        // Fix start tags
+        $html = preg_replace("/&lt;(($tags).*?)&gt;/i", '<$1>', $html);
+        // Fix end tags
+        $html = preg_replace("/&lt;\/($tags)&gt;/i", '</$1>', $html);
+        // Fix quotes
+        $html = preg_replace("/&quot;/", '"', $html);
+        $html = addslashes($html);
+        // Don't allow, e.g. <a href="javascript:evil_code">
+        $html = preg_replace("/<($tags)([^>]*)>/ie", "'<$1' . stripslashes_safe(str_replace('javascript', 'hackerscript', '$2')) .'>'", $html);
+        // Don't allow, e.g. <img src="foo.gif" onmouseover="evil_javascript">
+        $html = preg_replace("/<($tags)([^>]*)>/ie", "'<$1' . stripslashes_safe(str_replace(' on', ' off', '$2')) .'>'", $html);
+        $html = stripslashes_safe($html);
+                                                                            
+    }
+                                                                                                     
+    // restore the ampersands
+    $html = preg_replace('/\0/', '&', $html);
+          
+    return($html);
+} // safeHTML
+
+// I don't like how the PHP count() function returns 1 if
+// you pass it a scalar. So this is my custom function that
+// will return 0 if the argument isn't an array.
+function blog_array_count($arr) {
+    if (!is_array($arr)) {
+        return 0;
+    }
+    
+    return count($arr);
+}
+
+/**
+* check_dir_exists
+ *   Function to check if a directory exists
+ *    and, optionally, create it
+ *    copied from moodle/backup/lib.php
+ */
+if (! function_exists('check_dir_exists')) {
+    function check_dir_exists($dir, $create=false) {
+        
+        global $CFG; 
+        
+        $status = true;
+        if (!is_dir($dir)) {
+            if (!$create) {
+                $status = false;
+            } else {
+                umask(0000);
+                $status = mkdir ($dir, $CFG->directorypermissions);
+            }
+        }
+        return $status;
+    }
+}
+
+/////////////// Time and Date display functions ///////////////
+
+/*
+ * Returns the current time as a readable date string
+ * using moodle's chosen full date display format from admin configuration.
+ */
+function blog_now() {
+    $strftimedaydatetime = get_string('strftimedaydatetime');
+    $date = userdate(time(), $strftimedaydatetime);
+    return $date;
+}
+
+/**
+* UNIX timestamp to a readable format.
+ * using moodle's chosen date format from admin configuration.
+ */
+function blog_format_date($datetime) {
+    $strftimedate = get_string('strftimedate');
+    $date = userdate($datetime, $strftimedate);
+    return $date;
+}
+
+/**
+* converts unix timestamp to just a date
+ * using moodle's chosen short date format from admin configuration.
+ */
+function blog_short_date($datetime) {
+    $strftimedateshort = get_string('strftimedateshort');
+    $date = userdate($datetime, $strftimedateshort);
+    return $date;     
+}
+
+/**
+* converts unix timestamp to just a date
+ * using moodle's chosen time format from admin configuration.
+ */
+function blog_short_time($datetime) {
+    $strftimetime = get_string('strftimetime');
+    $time = userdate($datetime, $strftimedateshort);
+    return $time;
+}
+
+/////////////// Trackback functions ///////////////
+
+// Note: trackback specification
+// http://www.movabletype.org/docs/mttrackback.html
+
+
+/**
+ * generate rdf for trackback autodiscovery
+ */
+function blog_get_trackback_rdf_string($blogEntry) {
+    
+    global $CFG;
+    $userid = $blogEntry->entryuserid;
+    $entryid = $blogEntry->entryId;
+    $blogInfo = new BlogInfo($userid);
+    
+//    echo 'in blog_get_trackback_rdf_string blogEntry:<br />'."\n"; //debug
+//    print_object($blogEntry); //debug
+    
+    $rdf = "\n".'<!-- //RDF for trackback autodiscovery
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:dc="http://purl.org/dc/elements/1.1/"
+         xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
+<rdf:Description
+    rdf:about="'. htmlentities($CFG->wwwroot .'/blog/archive.php?userid='. $userid .'&amp;postid='. $entryid) .'"
+    dc:identifier="'. htmlentities($CFG->wwwroot .'/blog/archive.php?userid='. $userid .'&amp;postid='. $entryid) .'"
+    dc:title="'. $blogInfo->blogtitle .'" 
+    trackback:ping="';
+    if ($CFG->slasharguments) {
+               $rdf .= $CFG->wwwroot .'/blog/tb.php/'. $entryid;
+    } else {
+        $rdf .= $CFG->wwwroot .'/blog/tb.php?file=/'. $entryid;
+    }
+    $rdf .='" />
+</rdf:RDF>
+-->'."\n";
+    unset($blogInfo); //clean up after ourselves
+    return $rdf;    
+}
+
+
+/**
+ * Send a trackback to another server
+ */
+function blog_send_tb_ping($tb_arr) {
+    // import the http class
+    include_once('class.HttpClient.php');
+
+//     print "debug: in blog_send_tb_ping tb_arr = <br />"; //debug
+//     print_object($tb_arr); //debug
+
+    // Make sure the excerpt is short enough
+    $body = strip_tags($tb_arr['excerpt']);
+    $tb_arr['excerpt'] = substr($body, 0, 255) ."...";
+
+    // extract url
+//    $tb_url = $tb_arr['url'];
+    // extract url -- Daryl Hawes note: isn't this supposed to be tb_url we're extracting here?
+    $tb_url = $tb_arr['tb_url'];
+
+//     print "debug: tb_url:<br />"; //debug
+//     print_object($tb_url); //debug
+    
+    // Did we get a trackback url?
+    if (!$tb_url) {
+        // Couldn't find a Trackback url. Give up.
+        return;
+    }
+
+    // remove it from the array
+    unset($tb_arr['tb_url']);
+
+    // parse the TB url to get the host and path
+    $urlobj = parse_url($tb_url);
+
+//     print "debug: urlobj:<br />";
+//     print_object($urlobj);
+
+    // Create the http client object
+    $http_client = new HttpClient($urlobj['host']);
+
+    $postPath = $urlobj['path'];
+    if ( isset($urlobj['query']) ) {
+        $postPath = $postPath.'?'.$urlobj['query'];
+    }
+
+    // Post the data
+    $status = $http_client->post($postPath, $tb_arr);  
+
+    if ($status == true) {
+        $rsp = $http_client->getContent();
+    } else {
+        $rsp = get_string('error').': '.$http_client->getError();
+    }
+    return $rsp;
+}
+
+/**
+ * Return a list of trackbacks for a particular id
+ */
+function blog_list_trackbacks($postid) {
+    global $db, $CFG;
+
+    //Daryl Hawes note: convert this sql statement to a moodle function call
+    $sql = 'SELECT * FROM '. $CFG->prefix .'blog_trackback WHERE entryid = '. $postid;
+    $res = $db->Execute($sql);
+
+    //iterate results
+    $list = array();
+    while( !$res->EOF && isset($res->fields) ) {
+        $list[] = $res->fields;
+        $res->MoveNext();
+    }
+
+    return $list;
+}
+
+/**
+ * Return a raw count of how many trackbacks an entry has.
+ */
+function blog_tb_count($id) {
+    $list = list_trackbacks($id);
+
+    return array_count($list);
+}
+
+/**
+ * Display HTML formatted list of trackbacks for the supplied entryid
+ * called from tb.php if mode == list, also called by archive.php
+ * @param int id the entry in question by id
+ */
+function blog_print_trackbacks_for_id($id) {
+    global $CFG;
+    $list = blog_list_trackbacks($id);
+    if (! blog_array_count($list)) {
+        // There are not trackbacks for this entry
+        // To avoid page clutter return without printing anything
+        return;
+    }
+    if ($CFG->slasharguments) {
+               $tburl = $CFG->wwwroot .'/blog/tb.php/'. $id;
+    } else {
+        $tburl = $CFG->wwwroot .'/blog/tb.php?file=/'. $id;
+    }
+    
+    print '<div class="trackbacks">'."\n";
+    print '<h4>'. get_string('trackbacks', 'blog') .'</h4>';
+    // print '<p>'. get_string('tburlis', 'blog') .':'."\n";
+    //print '<!--<a href="'. $tburl .'">-->'."\n";
+    //print $tburl;
+    //print '<!--</a>-->'."\n</p>";
+    print '<ul>';
+/*
+    if (!blog_array_count($list)) {
+        // No trackbacks
+        ?>
+        <li><?php print_string('notrackbacks', 'blog'); ?>.</li>
+        <?php
+    } else {
+*/
+        while (list($row, $data) = each($list)) {
+            $post_title = stripslashes_safe($data['title']);
+            $excerpt = stripslashes_safe($data['excerpt']);
+            $url = stripslashes_safe($data['url']);
+            $blogname = stripslashes_safe($data['blogname']);
+            $added = blog_format_date($data['lastmodified']);
+
+            if ($blogname) {
+                $blogname = ' '. get_string('tbfrom', 'blog') .' '. $blogname;
+            }
+
+            ?><li>
+            <a href="<?php echo $url; ?>"><?php echo $post_title; ?></a>
+            <cite><?php echo $blogname; ?> <?php echo $added; ?></cite>
+            <blockquote cite="<?php echo $url; ?>" title="<?php print_string('tbquotedfrom', 'blog'); ?>
+<?php echo $blogname; ?>">
+            <?php echo $excerpt; ?>
+            </blockquote>
+            </li>
+            <?php
+        }
+//    }
+    print '</ul></div>'."\n";
+
+}
+
+/**
+ * Display RSSized list
+ *daryl hawes note: what the heck does this do? What is the $list var?
+ * it's called from tb.php if mode == rss
+ */
+function blog_tb_rss($id) {
+    $list = blog_list_trackbacks($id);
+}
+
+/**
+ * Fetch urls in entry and attempt to auto-discover a TB link
+ */
+function blog_tb_autodiscover($text) {
+    global $CFG;
+    require_once($CFG->libdir .'/rsslib.php'); //for rss_unhtmlentities()
+    include_once('class.HttpClient.php');
+
+    preg_match_all("/(((http(s?):\/\/)|(www\.))([\-\_\w\.\/\#\?\+\&\=\%\;]+))/i", $text, $matches);
+
+    $tb_urls = array();
+
+//     echo "Debug: blog_tb_autodiscover - matches:<br />"; //debug
+//    print_object($matches[0]); //debug
+
+    foreach($matches[0] as $url) {
+        $contents = HttpClient::quickGet($url);
+
+//             echo "Debug: contents from quick get: $contents<br />"; //debug
+
+        if (preg_match_all("/(<rdf:RDF.*?<\/rdf:RDF>)/si", $contents, $m)) {
+            foreach($m[0] as $rdf) {
+                preg_match("/dc:identifier=\"([^\"]+)\"/", $rdf, $m2);
+                if (rss_unhtmlentities($m2[1]) == $url) {
+                    if (preg_match("/trackback:ping=\"([^\"]+)\"/", $rdf, $m3)) {
+                        if (!in_array($m3[1], $tb_urls)) {
+                            array_push($tb_urls, $m3[1]);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return $tb_urls;
+}
+
+function blog_tblink($id, $date, &$blogEntry) {
+    $tblist = blog_list_trackbacks($id);
+    $count = blog_array_count($tblist);
+    $post_link = $blogEntry->get_simple_entry_link();
+    $msg = '<a href="'. $post_link .'">'. get_string('trackbacks', 'blog') .'('. $count .')</a>';
+    return $msg;
+}
+
+
+
+/////////////////////// CATEGORY MANAGEMENT ////////////////////////////////////
+
+
+
+  
+/**
+ *called by blog_category_list
+ * @return string  html links for each category
+ */
+function blog_get_html_display_for_categories(&$blogFilter, $shownumentries, &$records, $showseparators, $title, $section) {
+    global $CFG, $editing;
+    $returnstring ='';
+    if ($showseparators) {
+        //first show a separator if requested
+        $returnstring .= $title .'<br />'."\n";
+    }
+    $editallowed = false;
+
+    if ($editing) {
+        $isteacher = false;
+        if (isset($blogFilter->courseid)) {
+            $isteacher = isteacher($blogFilter->courseid);
+        }
+        if ( isadmin() ) {
+            // admin is allowed to edit any categories on the site
+            $editallowed = true;
+        } else if ( ($section == 'course' || $section == 'group') && $isteacher ) {
+            // teacher of course can modify course categories and group categories
+            $editallowed = true;
+        } else if ($section == 'personal' && blog_is_blog_admin($blogFilter->userid) ) {
+            // user can modify their own blog categories
+            $editallowed = true;
+        }
+    }
+
+    if (!isset($records) ) {
+        return;
+    }
+    
+    foreach($records as $record) {
+        $catcount = '';
+        $categoryid = $record->id;
+        $categoryname = $record->catname;
+        if ($shownumentries) {
+            $tempfilter =& new BlogFilter('', $categoryid);
+            $catcount = ' (';
+            $catcount .= $tempfilter->get_filtered_entry_count();
+            $catcount .= ')';
+        }
+        $blogFilter->categoryid = $categoryid;
+        $returnstring .= $blogFilter->get_complete_link($CFG->wwwroot .'/blog/index.php', stripslashes_safe($categoryname) . $catcount);
+        if ($editallowed) {
+            // note that only the 'act' and 'categoryid' vars are needed here because the me() function includes the 
+            // existing query string
+            $returnstring .= '&nbsp;<a href="'. me() .'&amp;act=editcategory&amp;categoryid='. $categoryid .'">';
+            $returnstring .= '<img src="'. $CFG->pixpath .'/t/edit.gif" alt="'. get_string('edit');
+            $returnstring .= '" title="'. get_string('edit') .'" align="absmiddle" height="16" width="16" border="0" /></a>'."\n";
+            if ($categoryid != 1) { //do not remove "General" sitewide category
+                $returnstring .= '&nbsp;<a href="'. $CFG->wwwroot .'/blog/admin.php?act=delcategory&amp;categoryid='. $categoryid;
+                $returnstring .= '&amp;userid='. $blogFilter->userid .'&amp;courseid='. $blogFilter->courseid .'&amp;groupid='. $blogFilter->groupid .'" onClick="return confirm(\''. get_string('confirmcategorydelete', 'blog') .'\');">';
+                $returnstring .= '<img src="'. $CFG->pixpath .'/t/delete.gif" ALT="'. get_string('delete');
+$returnstring .= '" title="'. get_string('delete') .'" align="absmiddle" border="0" /></a>'."\n";
+            }
+        }
+        $returnstring .= '<br />'."\n";
+    }
+    return $returnstring;
+}
+
+/**
+ *
+ */
+function blog_get_popup_display_for_categories(&$blogFilter, $format, $shownumentries, &$records) {
+    global $CFG;
+
+    if (!isset($records) ) {
+        return;
+    }
+    $returnstring = '';
+    
+    foreach ($records as $record) {
+        if ($format == 2) {
+            $value = $CFG->wwwroot .'/blog/index.php?categoryid='. $record->id;
+            $value .= '&amp;userid='. $blogFilter->userid .'&amp;courseid='. $blogFilter->courseid .'&amp;groupid='. $blogFilter->groupid;
+        } else {
+            $value = $record->id;
+        }
+        
+        $returnstring .= '<option value="'. $value .'"';
+        if ($record->id == $blogFilter->categoryid) {
+            $returnstring .= ' selected';
+        }
+        $catcount = '';
+        $categoryid = $record->id;
+        $categoryname = $record->catname;
+        if ($shownumentries) {
+            $tempfilter =& new BlogFilter('', $categoryid);
+            $tempfilter->userid = $blogFilter->userid;
+            $catcount = ' (';
+            //if we had an array of blogentry objects we could avoid a database call
+            //and instead simply ask the blogentry objects to tell us which apply
+            $catcount .= $tempfilter->get_filtered_entry_count();
+            $catcount .= ')';
+        }
+        $returnstring .= '>' ."\n". stripslashes_safe($categoryname) . $catcount ."\n";
+        $returnstring .= '</option>';
+    }
+    return $returnstring;
+}
+?>
diff --git a/blog/preferences.html b/blog/preferences.html
new file mode 100755 (executable)
index 0000000..68d8369
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+    // print out the extra form element needed to support post previews
+    blog_print_preview_form($bloginfo->userid);
+?>
+
+<form method="post" action="preferences.php" name="entry" id="entry">
+
+<table cellpadding="9" cellspacing="0">
+<tr valign="top">
+    <td><?php print_string('title', 'blog');?>:</td>
+    <td><input type="text" size="20" maxlength="32" name="blogtitle" value="<?php echo $bloginfo->get_blog_title();?>" /></td>
+</tr>
+<tr>
+    <td><?php print_string('tagline', 'blog');?>:</td>
+    <td><input type="text" size="32" maxlength="128" name="blogtagline" value="<?php echo $bloginfo->get_blog_tagline();?>" /></td>
+</tr>
+
+<tr>
+    <td><?php print_string('pagesize', 'blog');?>:</td>
+    <td><input type="text" size="32" maxlength="128" name="pagesize" value="<?php echo get_user_preferences('blogpagesize',8);?>" />
+    </td>
+</tr>
+<?php  if (!empty($CFG->allowblogthemes)) { ?>
+<tr valign="top">
+    <td align="right"> <?php print_string("forcetheme") ?>: </td>
+    <td>
+      <?php
+         $themes[''] = get_string("forceno");
+         $themes += get_list_of_themes();
+         choose_from_menu($themes, 'theme', $bloginfo->get_blog_theme(), "", "", "");
+      ?>
+    </td>
+</tr>
+<?php } ?>
+<tr>
+    <td colspan="2" align="center">
+
+    <input type="hidden" name="format" value="1" />
+    <input type="hidden" name="etitle" value="This is a preview of this template" />
+    <input type="hidden" name="body" value="Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure d" />
+
+       <input type="submit" value="<?php print_string("savechanges") ?>" /></td>
+</tr>
+</table>
+</form>
diff --git a/blog/preferences.php b/blog/preferences.php
new file mode 100755 (executable)
index 0000000..ac72d49
--- /dev/null
@@ -0,0 +1,73 @@
+<?php  // $Id$
+       // preferences.php - user prefs for blog modeled on calendar
+
+    require_once('../config.php');
+    require_once($CFG->dirroot.'/blog/lib.php');
+
+    require_login();
+    global $USER;
+
+    // detemine where the user is coming from in case we need to send them back there
+    if (isset($_SERVER['HTTP_REFERER'])) {
+        $referrer = $_SERVER['HTTP_REFERER'];
+    } else {
+        $referrer = $CFG->wwwroot;
+    }
+
+    //ensure that the logged in user is not using the guest account
+    if (isguest()) {
+        error(get_string('noguestpost', 'forum'), $referrer);
+    }
+    
+    if (!blog_isLoggedIn() ) {
+        error(get_string('noguestpost', 'forum'), $referrer);
+    }
+    $userid = $USER->id;
+    $bloginfo =& new BlogInfo($userid);
+
+/// If data submitted, then process and store.
+
+       if ($post = data_submitted()) {
+        print_header();
+        if (!isset($post->blogtitle) || !isset($post->blogtagline) ) {
+            error(get_string('settingsupdatederror', 'blog'), $referrer);
+        }
+
+        if (! $bloginfo->set_blog_title($post->blogtitle)) {
+            error(get_string('settingsupdatederror', 'blog'), $referrer );
+        }
+
+        if (! $bloginfo->set_blog_tagline($post->blogtagline)) {
+            error(get_string('settingsupdatederror', 'blog'), $referrer );
+        }
+    
+        if (isset($post->theme)) {
+            if (! $bloginfo->set_blog_theme($post->theme)) {
+                error(get_string('settingsupdatederror', 'blog'), $referrer );
+            }
+        }
+        
+        set_user_preference('blogpagesize', optional_param('pagesize'));
+        
+        redirect($referrer, get_string('changessaved'), 1);
+        exit;
+    }
+    
+    $site = get_site();
+    $pageMeta = '<script language="javascript" type="text/javascript" src="'. $CFG->wwwroot .'/blog/blog.js"></script>' . "\n";
+
+    $strpreferences = get_string('preferences', 'calendar');
+
+    $navigation = '<a href="'. $bloginfo->get_blog_url() .'">'. $bloginfo->get_blog_title() . '</a> -> '. $strpreferences;
+
+    print_header("$site->shortname: ". $bloginfo->get_blog_title() .": $strpreferences", $bloginfo->get_blog_title(), $navigation, '', $pageMeta, true, '', '');
+
+    print_heading($strpreferences);
+
+    print_simple_box_start('center', '', '');
+
+       include('./preferences.html');
+    print_simple_box_end();
+
+    print_footer();
+?>
diff --git a/blog/set_session_vars.php b/blog/set_session_vars.php
new file mode 100644 (file)
index 0000000..f0315a8
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+/// inspired/taken from moodle calendar module's set.php file
+
+    require_once('../config.php');
+    require_once('lib.php');
+
+    require_variable($_GET['referrer']);
+    require_variable($_GET['var']);
+    optional_variable($_GET['value']);
+    optional_variable($_GET['userid']);
+    optional_variable($_GET['courseid']);
+    optional_variable($_GET['categoryid']);
+    optional_variable($_GET['d']);
+    optional_variable($_GET['m']);
+    optional_variable($_GET['y']);
+
+    switch($_GET['var']) {
+        case 'setcourse':
+            $id = intval($_GET['id']);
+            if($id == 0) {
+                $SESSION->cal_courses_shown = array();
+                calendar_set_referring_course(0);
+            }
+            else if($id == 1) {
+                $SESSION->cal_courses_shown = calendar_get_default_courses(true);
+                calendar_set_referring_course(0);
+            }
+            else {
+                // We don't check for membership anymore: if(isstudent($id, $USER->id) || isteacher($id, $USER->id)) {
+                if(get_record('course', 'id', $id) === false) {
+                    // There is no such course
+                    $SESSION->cal_courses_shown = array();
+                    calendar_set_referring_course(0);
+                }
+                else {
+                    calendar_set_referring_course($id);
+                    $SESSION->cal_courses_shown = $id;
+                }
+            }
+        break;
+        case 'setcategory':
+        break;
+        case 'setblog':
+        break;
+        case 'showediting':
+            $SESSION->blog_editing_enabled = !$SESSION->blog_editing_enabled;
+        break;
+    }
+    redirect($referrer);
+?>
\ No newline at end of file
diff --git a/blog/tags.html b/blog/tags.html
new file mode 100755 (executable)
index 0000000..62ffb06
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+print_heading(get_string('tagsmanagement','blog'));
+?>
+
+<table>
+    <tr>
+        <td width="50%">Official Tags</td>
+        <td width="50%">User Defined Tags</td>
+    </tr>
+    
+    <tr>
+        <td>
+            <form action="tags.php" method="POST">
+            <?php if (isadmin()){ ?>
+            <select name="tags[]" multiple="multiple" size="8">
+            <?php
+                $otags = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'official\' ORDER by text ASC');
+                foreach ($otags as $otag) {
+                    echo '<option value="'.$otag->id.'">'.$otag->text.'</option>';
+                }
+            ?>
+            </select>
+            <input type="hidden" name="mode" value="delete" />
+            <br />
+            <input type="submit" value="delete" />
+            <?php }
+                else {
+                    $otags = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'official\' ORDER by text ASC');
+                    foreach ($otags as $otag) {
+                        echo '<br />'.$otag->text;
+                    }
+                }
+            ?>
+            </form>
+        </td>
+        
+        <td>
+            <form action="tags.php" method="POST">
+            <select name="tags[]" multiple="multiple" size="8">
+            <?php
+                $ptags1 = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'personal\' AND userid = '.$USER->id.' ORDER by text ASC');
+                $ptags2 = get_records_sql('SELECT * from '.$CFG->prefix.'tags WHERE type=\'personal\' AND userid <> '.$USER->id.' ORDER by text ASC');;
+                $ptags = array_merge($ptags1, $ptags2);
+                foreach ($ptags as $ptag) {
+                    echo '<option value="'.$ptag->id.'">'.$ptag->text.'</option>';
+                }
+            ?>
+            </select>
+            <input type="hidden" name="mode" value="delete" />
+            <br />
+            <input type="submit" value="delete" />
+            </form>
+        </td>
+    </tr>
+    
+    <tr>
+        <td>
+            <?php if (isadmin()) { ?>
+            <form action="tags.php" method="POST">
+            Add Official:<br/>
+            <input type="text" name="otag" />
+            <input type="submit" value="add" />
+            <input type="hidden" name="mode" value="addofficial" />
+            </form>
+            <?php }?>
+        </td>
+        
+        <td>
+            <form action="tags.php" method="POST">
+            Add Personal:<br/>
+            <input type="text" name="ptag" />
+            <input type="submit" value="add" />
+            <input type="hidden" name="mode" value="addpersonal" />
+            </form>
+        </td>
+    </tr>
+    
+</table>
+
+<p align="center">
+    <a href="#" onclick="window.close()">close this window</a>
+</p>
diff --git a/blog/tags.php b/blog/tags.php
new file mode 100755 (executable)
index 0000000..e539b9a
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+require_once('../config.php');
+
+//form process
+
+$mode = optional_param('mode','',PARAM_ALPHA);
+
+switch ($mode) {
+    case 'addofficial':
+        if (($otag = optional_param('otag')) && (!get_record('tags','text',$otag))) {
+            $tag->userid = $USER->id;
+            $tag->text = $otag;
+            $tag->type = 'official';
+            $tagid = insert_record('tags', $tag);
+            echo '<script language="JavaScript" type="text/javascript">
+            var o = opener.document.createElement("option");
+            o.innerHTML = "<option>'.$tag->text.'</option>";
+            o.value = '.$tagid.';
+            opener.document.entry[\'otags[]\'].insertBefore(o, null);
+            </script>';
+        } else {
+            error ('there is already a tag with this name!');
+        }
+
+    break;
+    
+    case 'addpersonal':
+        if (($ptag = optional_param('ptag')) && (!get_record('tags','text',$ptag))) {
+            $tag->userid = $USER->id;
+            $tag->text = $ptag;
+            $tag->type = 'personal';
+            $tagid = insert_record('tags', $tag);
+            echo '<script language="JavaScript" type="text/javascript">
+            var o = opener.document.createElement("option");
+            o.innerHTML = "<option>'.$tag->text.'</option>";
+            o.value = '.$tagid.';
+            opener.document.entry[\'ptags[]\'].insertBefore(o, null);
+            </script>';
+        } else {
+            error ('there is already a tag with this name!');
+        }
+        //write back to window.opener
+    break;
+    
+    case 'delete':
+        $tags = optional_param('tags');
+        print_object($tags);
+        foreach ($tags as $tag) {
+
+            $blogtag = get_record('tags','id',$tag);
+            
+            if (!isadmin() and $USER->id != $blogtag->userid) {
+                notify('no right to delete');
+                continue;
+            }
+            
+            if (!isadmin() && get_records('blog_tag_instance','tagid', $tag)) {
+                notify('tag is used by other users, can not delete!');
+                continue;
+            }
+            
+            delete_records('tags','id',$tag);
+            delete_records('blog_tag_instance', 'tagid', $tag);
+
+            //remove parent window option
+            echo '<script>
+            var i=0;
+            while (i < window.opener.document.entry[\'otags[]\'].length) {
+                if (window.opener.document.entry[\'otags[]\'].options[i].value == '.$tag.') {
+                    window.opener.document.entry[\'otags[]\'].removeChild(opener.document.entry[\'otags[]\'].options[i]);
+                }
+                i++;
+            }
+            
+            var i=0;
+            while (i < window.opener.document.entry[\'ptags[]\'].length) {
+                if (window.opener.document.entry[\'ptags[]\'].options[i].value == '.$tag.') {
+                    window.opener.document.entry[\'ptags[]\'].removeChild(opener.document.entry[\'ptags[]\'].options[i]);
+                }
+                i++;
+            }
+
+            </script>';
+        }
+        //write back to window.opener
+    break;
+    
+    default:
+    break;
+}
+
+//print the table
+
+print_header();
+
+include_once('tags.html');
+
+print_footer();
+
+
+?>
diff --git a/blog/version.php b/blog/version.php
new file mode 100644 (file)
index 0000000..a90ced3
--- /dev/null
@@ -0,0 +1,10 @@
+<?php // $Id$
+
+/////////////////////////////////////////////////////////////////////////////////
+///  Code fragment to define the version of the blog module
+///  This fragment is called by moodle_needs_upgrading() and /admin/index.php
+/////////////////////////////////////////////////////////////////////////////////
+
+$blog_version  = 2005030400;  // The current version of blog module (Date: YYYYMMDDXX)
+$module->cron     = 1800;           // Period for cron to check this module (secs)
+?>