The CUNY Academic Commons is hiring a developer

The CUNY Academic Commons is hiring!

We are looking for a part-time developer to join our growing development team (part-time = starting around 5hrs/wk, with the potential for growth). Developing for the Commons means fixing bugs, responding to feature requests, and building things from scratch. To get a sense of the sort of work we do, have a look at our public bug tracker and our page on the wordpress.org plugin repository.

Here’s a little bit about the kind of developer we are looking for.

You must have:

  • Extensive experience developing for WordPress (where ‘developing’ means coding from scratch)
  • Good communication and collaboration skills (the job involves working with end-users as well as fellow developers)
  • Experience using and developing for BuddyPress
  • A solid understanding of front-end best practices, including proper markup, JavaScript libraries like jQuery, cross-browser testing of CSS

The ideal candidate will also have:

  • A proven track record of participation in open-source communities, in the form of: patches submitted; plugins or themes available for public use; activity on blogs and forums related to open source projects (ideally in the WordPress world)
  • Knowledge of Git
  • Experience administering and developing for MediaWiki
  • Some knowledge of system administration – MySOL, Apache, Red Hat Linux

We’re a close-knit development team. Special consideration will be given to applicants who are local to the New York City area.

If you’re interested, send an email to [email protected] containing a statement about why you’d be a good fit for the job, as well as links to one or more of the following:

  • Websites you have built or helped to build, with an explanation of the role you played in the construction of the site
  • Publicly available code (your Github account, your plugins page in the wordpress.org repo, your contributions to the source of a large project)
  • Your blog or website

We’re hoping to move quickly, so please don’t delay in sending your applications. We’d like to do a first round of interviews (probably via Skype for non-locals) around the beginning of April.

ABOUT THE CUNY ACADEMIC COMMONS

The CUNY Academic Commons https://commons.gc.cuny.edu is a website whose mission is to provide a platform for connection and collaboration between faculty members, administration, graduate students, and staff across the 23 campuses of the City University of New York. The Commons has been a leader in the development of open-source tools for social networking and academic work, contributing significant add-ons for software like BuddyPress and WordPress.

Commons 1.1.10

I have just released version 1.1.10 of the CUNY Academic Commons. About a month has passed since 1.1.9, and in that time, a lot of small fixes and enhancements have taken place. Of note:

  • WordPress and BuddyPress have been updated to the most recent versions (3.1 and 1.2.8, respectively)
  • New WP theme: Bibliotype
  • New WP plugins: ZotPress (for integration with your Zotero library); List Pages Shortcode (for displaying page hierarchy on your blog)
  • Fixes to the way that group blog membership is synched with group membership
  • Sticky forum posts fixed so that stickies don’t get pushed down to the second page of a group forum

You can read about the release in a more in-depth way by checking out the 1.1.10 milestone.

New BuddyPress plugin: BuddyPress Docs

When I explain the CUNY Academic Commons to someone for the first time, the words ‘connect’ and ‘collaborate’ usually loom large. We provide a number of tools to make it easier for people to find each other (that’s the connecting), and then we try to make it easy for those people to work together on projects that matter to them (that’s the collaborating). Today I am releasing the first public beta of a new BuddyPress plugin that will, I hope, be an important tool in the Commons (and BuddyPress) collaboration toolbox: BuddyPress Docs.

BuddyPress Docs adds a new tab to groups where members can collectively create and edit documents, using an easy-to-use rich text editor. Docs support tagging, hierarchy, oEmbed multimedia, and much more. I’ve written about the feature list in detail on the BuddyPress Docs homepage.

Today the project is being released in a near-stable public beta. That means a few things. First, for the moment you run the plugin on a production site at your own risk, as there are bound to be bugs and rough spots (which I hope you will report back to me!). Second, it means that it’s not up and running on the Commons quite yet. BuddyPress Docs is slated to be turned on here with the release of Commons 1.2, which should be sometime in the next few weeks. The intervening weeks should give folks in the general BuddyPress community some time to put Docs through its paces before it gets put to work here.

The official documentation states that BuddyPress Docs requires WordPress 3.1 and BuddyPress 1.3. The latter requirement is a bit of an overstatement; Docs has been tested with BuddyPress 1.2.8 and it works well. However, the former requirement is strict: though the plugin might appear to work with versions of WP prior to 3.1, certain key features will not work (in particular, taxonomies will not work correctly, so that all Docs will appear on every group, no matter which group created them). Perhaps for the 1.0 stable release I’ll forcibly prevent the plugin from being loaded on those earlier versions of WP.

You can follow the plugin’s development at http://github.com/boonebgorges/buddypress-docs.

Hardening BuddyPress Group Documents

The BuddyPress Group Documents plugin allows groups a handy way for users to share documents with fellow members of a BP group. It’s crucial to the work that is done on the CUNY Academic Commons. But, by default, the plugin stores documents in a subdirectory of your WP uploads folder (usually /wp-content/blogs.dir/ on multisite). That means that documents are available directly, to anyone who has the URL, regardless of the public/private/hidden status of groups. This isn’t a problem from within BuddyPress, since URLs for the documents only appear inside of the protected group interface. But if the URL is shared, then the document becomes publicly available. Worse, if someone posts the URL of a document in a public place, search engine bots will find it, and the contents of the document could end up in Google.

I wrote a few helper functions to change this behavior. The strategy is this: Move the files so that they are not accessible via URL, ie in a directory above the web root. (In my case, it’s a directory called bp-group-documents, just above my web root.) Then, catch requests of a certain type (I’ve chosen to go with a URL parameter get_group_doc=), and check them to see whether the current user has the adequate permission to access the document in question. Finally, make sure that all of the URLs and paths that BPGD uses to upload and display documents are filtered to the updated versions. I’ve provided my code below – use and modify at your pleasure. You should be able to place it in your plugins/bp-custom.php file, and then move your existing docs from their current location (probably something like wp-content/blogs.dir/1/files/group-documents) to the new directory.

I also added a line to my .htaccess file to ensure that requests to the old URLs are redirected to the new, hardened URL. That line is this:


RewriteRule ^wp\-content/blogs\.dir/1/files/group\-documents/(.*) /?get_group_doc=$1 [R,L]
 

Obviously, you may have to modify it for different file paths.

EDITED Feb 8, 2011 to include the code for creating directories when none exist


define( 'BP_GROUP_DOCUMENTS_SECURE_PATH', substr( ABSPATH, 0, strrpos( rtrim( ABSPATH, '/' ), '/' ) ) . '/bp-group-documents/' );

function cac_filter_doc_url( $doc_url, $group_id, $file ) {
	$url = bp_get_root_domain() . '?get_group_doc=' . $group_id . '/' . $file;
	return $url;
}
add_filter( 'bp_group_documents_file_url', 'cac_filter_doc_url', 10, 3 );

function cac_filter_doc_path( $doc_url, $group_id, $file ) {
	$document_dir = BP_GROUP_DOCUMENTS_SECURE_PATH . $group_id . '/';
	
	if ( !is_dir( $document_dir ) )
		mkdir( $document_dir, 0775, true );

	$path = BP_GROUP_DOCUMENTS_SECURE_PATH . $group_id . '/' . $file;
	return $path;
}
add_filter( 'bp_group_documents_file_path', 'cac_filter_doc_path', 10, 3 );

function cac_catch_group_doc_request() {
	$error = false;

	if ( empty( $_GET['get_group_doc'] ) )
		return;
	
	$doc_id = $_GET['get_group_doc'];
	
	// Check to see whether the current user has access to the doc in question
	$file_deets 	= explode( '/', $doc_id );
	$group_id 	= $file_deets[0];	
	$group		= new BP_Groups_Group( $group_id );
	
	if ( empty( $group->id ) ) {
		$error = array(
			'message' 	=> 'That group does not exist.',
			'redirect'	=> bp_get_root_domain()
		);
	} else {
		if ( $group->status != 'public' ) {
			// If the group is not public, then the user must be logged in and
			// a member of the group to download the document
			if ( !is_user_logged_in() || !groups_is_user_member( bp_loggedin_user_id(), $group_id ) ) {
				$error = array(
					'message' 	=> sprintf( 'You must be a logged-in member of the group %s to access this document. If you are a member of the group, please log into the site and try again.', $group->name ),
					'redirect'	=> bp_get_group_permalink( $group )
				);
			}
		}
		
		// If we have gotten this far without an error, then the download can go through
		if ( !$error ) {
			
			$doc_path = BP_GROUP_DOCUMENTS_SECURE_PATH . $doc_id;
			
			if ( file_exists( $doc_path ) ) {
				$mime_type = mime_content_type( $doc_path );
				$doc_size = filesize( $doc_path );
				
				header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
				header("Pragma: hack");
					
				header("Content-Type: $mime_type; name='" . $file_deets[1] . "'");
				header("Content-Length: " . $doc_size );
				
				header('Content-Disposition: attachment; filename="' . $file_deets[1] . '"');
				header("Content-Transfer-Encoding: binary");              
				ob_clean();
				flush();  		
				readfile( $doc_path );
				die();
		       
			} else {
				// File does not exist
				$error = array(
					'message' 	=> 'The file could not be found.',
					'redirect'	=> bp_get_group_permalink( $group ) . '/documents'
				);
			}
		}
	}
		
	// If we have gotten this far, there was an error. Add a message and redirect
	bp_core_add_message( $error['message'], 'error' );
	bp_core_redirect( $error['redirect'] );
}
add_filter( 'wp', 'cac_catch_group_doc_request', 1 );

// http://www.php.net/manual/en/function.mime-content-type.php#87856
if(!function_exists('mime_content_type')) {

    function mime_content_type($filename) {

        $mime_types = array(

            'txt' => 'text/plain',
            'htm' => 'text/html',
            'html' => 'text/html',
            'php' => 'text/html',
            'css' => 'text/css',
            'js' => 'application/javascript',
            'json' => 'application/json',
            'xml' => 'application/xml',
            'swf' => 'application/x-shockwave-flash',
            'flv' => 'video/x-flv',

            // images
            'png' => 'image/png',
            'jpe' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'gif' => 'image/gif',
            'bmp' => 'image/bmp',
            'ico' => 'image/vnd.microsoft.icon',
            'tiff' => 'image/tiff',
            'tif' => 'image/tiff',
            'svg' => 'image/svg+xml',
            'svgz' => 'image/svg+xml',

            // archives
            'zip' => 'application/zip',
            'rar' => 'application/x-rar-compressed',
            'exe' => 'application/x-msdownload',
            'msi' => 'application/x-msdownload',
            'cab' => 'application/vnd.ms-cab-compressed',

            // audio/video
            'mp3' => 'audio/mpeg',
            'qt' => 'video/quicktime',
            'mov' => 'video/quicktime',

            // adobe
            'pdf' => 'application/pdf',
            'psd' => 'image/vnd.adobe.photoshop',
            'ai' => 'application/postscript',
            'eps' => 'application/postscript',
            'ps' => 'application/postscript',

            // ms office
            'doc' => 'application/msword',
            'rtf' => 'application/rtf',
            'xls' => 'application/vnd.ms-excel',
            'ppt' => 'application/vnd.ms-powerpoint',

            // open office
            'odt' => 'application/vnd.oasis.opendocument.text',
            'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
        );

        $ext = strtolower(array_pop(explode('.',$filename)));
        if (array_key_exists($ext, $mime_types)) {
            return $mime_types[$ext];
        }
        elseif (function_exists('finfo_open')) {
            $finfo = finfo_open(FILEINFO_MIME);
            $mimetype = finfo_file($finfo, $filename);
            finfo_close($finfo);
            return $mimetype;
        }
        else {
            return 'application/octet-stream';
        }
    }
}


 

Commons 1.1.8

I’ve just released version 1.1.8 of the CUNY Academic Commons. This upgrade was a bit bumpy – I actually pushed the update yesterday afternoon, but because of a few technical problems, I was not able to debug and tag the upgrade as stable. Thanks to some help from our delightful sysadmin @apitanga, we sorted things out and I was able to wrap up the release.

Commons 1.1.8 features a relatively large number of fixes and improvements:

  • Many plugins and themes, including BuddyPress, have been upgraded to the most recent versions
  • A large number of new themes have been added to the site
  • Embedding of Prezi and Slideshare presentations has been enabled
  • A bug blocking group members from being auto-added to the group’s blog has been fixed
  • A few other small bugs fixed

For complete details on this release, see the 1.1.8 milestone.

New WordPress plugin: Prezi WP

A member of the Commons community recently requested the ability to embed Prezi presentations into her Commons blog. I had a look at the options available in the wordpress.org repository, and the pickings were slim indeed. So I wrote my own plugin, Prezi WP. It’s pretty simple: use the [prezi] shortcode in your posts to embed a presentation. See the plugin homepage for more details on how to use the plugin, and how to download it and use it on your own WP installation.

Group Announcements tab in BuddyPress

I had a request or two to explain how I built the Group Announcements feature on the CUNY Academic Commons. Here goes.

Brief background: When the Commons was upgraded to BuddyPress 1.2, we got the benefit of interactive activity streams everywhere, including groups. This caused some confusion, however, as users were uncertain where conversation best fit into the Commons’s architecture: in the Forums (where it had been traditionally), or in the activity stream. In some communities this kind of fracturing might be okay or even welcome, but in ours it was confusing. At the same time, we wanted a way for group admins and mods to send important notices to the members of their groups. By taking the group activity updates and repurposing it as a Group Announcements section, I was able to kill two birds with one stone: providing an announcement space for mods, while focusing extended discussion in the forums.

You can download the CAC Group Announcements plugin here.

I’m not putting this in the repo at the moment because I don’t want to build a proper admin UI and support it 🙂 For that reason, here is a primer on how the plugin works – if you want to customize or maintain it, you’re on your own, buster.

  1. The CAC_Group_Announcements class is an instance of the BuddyPress Group Extension API. It is responsible for creating the Announcements tab markup and adding it to the nav. You’ll notice that the majority of the markup is created by including bp-default’s activity-loop.php and post-form.php templates. You could customize this more if you wanted.
  2. bp_is_group_announcements() is a little template tag that can be used to test whether you’re looking at a group announcements page. This is needed for the activity filter, in step 3.
  3. cac_set_announcement_filter() adds a filter to the bp_has_activities query string when you are looking at an announcements page, so that it only displays activity items of the type activity_update. In other words, when you are looking at the regular activity stream for the group, you see all of the associated group activity items (new members, forum posts, etc) but when you’re on the announcements page you only see activity updates (which, remember, have been repurposed as announcements).

As I look at the code, I see that there are things I would definitely change if I were going to make this into a distributed plugin. If you want to make those changes, be my guest. You’re welcome to help each other in the comment section, but I won’t be formally supporting this, as it is a very basic hack that should happen at the theme level anyway. Good luck!

Commons 1.1.7

Happy New Year! I have just released version 1.1.7 of the CUNY Academic Commons. This bugfix release contained a very important security release of WordPress (v3.0.4), as well as a few other small changes:

  • Bugfixes in the Google Calendar widget WP plugin
  • Bugfixes in the Cityscape WP theme
  • Installed the Meebo Me WP plugin

For complete details about the release, visit the 1.1.7 milestone.