{"id":898,"date":"2012-11-08T18:09:49","date_gmt":"2012-11-08T17:09:49","guid":{"rendered":"http:\/\/blog.soton.ac.uk\/oneshare\/?p=898"},"modified":"2012-11-08T18:09:49","modified_gmt":"2012-11-08T17:09:49","slug":"the-keywords-plugin","status":"publish","type":"post","link":"https:\/\/blog.soton.ac.uk\/oneshare\/2012\/11\/08\/the-keywords-plugin\/","title":{"rendered":"The Keywords plugin"},"content":{"rendered":"<p>The last plugin I&#8217;m going to talk about during this initial period of RedFeather development is the keywords plugin. \u00a0While the ability to add keywords to a resource is\u00a0desirable\u00a0enough to be a candidate for inclusion in the RedFeather core itself, I&#8217;ve decided to keep it separate for the time being since it serves as such an excellent demonstration of the plugin architecture. \u00a0It acts as an example implementation of many of the different features a plugin might provide, in this case:<\/p>\n<ol>\n<li>Modifying the metadata schema.<\/li>\n<li>Adding field to input workflow.<\/li>\n<li>Adding field to output views.<\/li>\n<li>Adding a new page.<\/li>\n<li>Adding a new toolbar item.<\/li>\n<\/ol>\n<p>So, let&#8217;s start by addressing the metadata schema. \u00a0This is stored in the Advanced Configuration section of the RedFeather source and is defined in the $CONF as a simple string array called &#8216;fields&#8217;. \u00a0However, this list by itself has no function and is merely a contract that defines what fields RedFeather will look for when it tries to perform actions involving metadata. \u00a0In other words, it is the responsibility of a RedFeather plugin to provide support for each field specified &#8211; fields themselves provide literally zero functionality on their own. \u00a0This is implemented using function pointers (like I mentioned in an earlier blog post, EVERYTHING is function pointers in RedFeather). \u00a0All a plugin has to do is loop through the fields and call a different handler function for each field in the schema. \u00a0In order to maintain code readability, each of these functions should be given a descriptive name following the RedFeather conventions.<\/p>\n<p>The RDF plugin for example, provides individual functions that understand how to convert the field data into triples. \u00a0This means that the function &#8220;generate_rdf_field_title&#8221; should return an RDF:description involving dc:title whereas the handler for processing the uploaded file needs to return both dct:hasPart and rdf:type. \u00a0This perfectly illustrates exactly why the metadata definition is entirely abstract &#8211; there is literally no way to automatically determine how to process any given field, and the additional code required to implement a system flexible enough to support this level of description outweigh any possible benefit it may give.<\/p>\n<p>In the case of the keywords plugin, this means implementing additional functions for the existing core modules: resource manager, browse\/view, rdf and rss. \u00a0The resource manager module is responsible for generating the edit workflow so this needs to be supported first. \u00a0The workflow generator automatically looks for functions in the form &#8216;generate_input_field_xxx&#8217;, which it concatenates to the bottom of the form. \u00a0There are no explicit restrictions on what these functions return as long as they include the required &lt;input&gt; elements for that field. \u00a0In simple cases, such as for the title, this leads to an incredibly simple, single-line definition. \u00a0However, the keywords field is actually a multifield (in that it can have more than one value) so is slightly more complicated. \u00a0Fortunately, RedFeather also provides a simple way to automatically support multifields, which is also utilised in the creators field.<\/p>\n<figure id=\"attachment_942\" aria-describedby=\"caption-attachment-942\" style=\"width: 767px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.19.57.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-942\" src=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.19.57.png\" alt=\"\" width=\"767\" height=\"513\" srcset=\"https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.19.57.png 767w, https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.19.57-300x200.png 300w\" sizes=\"auto, (max-width: 767px) 100vw, 767px\" \/><\/a><figcaption id=\"caption-attachment-942\" class=\"wp-caption-text\">Workflow with multifield keyword added.<\/figcaption><\/figure>\n<p>The browse\/view module is responsible for actually displaying this data, since it provides the implementation for the metadata generator. \u00a0This is implemented in a very similar way to the resource manager but instead looks for functions of the form &#8216;generate_output_field_xxx&#8217;. \u00a0This consistency when dealing with plugins and fields exists throughout RedFeather and is vital in maintaining code readability in a swamp of functions. \u00a0Output fields are much simpler than input fields and just have to return the html in the form of two-column table rows, which are combined to form the metadata table. \u00a0The RSS and RDF field implementations are also impressively compact so I will move on.<\/p>\n<p>That is all that is needed to implement the &#8216;data&#8217; aspect of the keywords plugin. \u00a0Appropriate input boxes now appear on the workflow and the fields manifest in the browse view, resource view, and alternative data views (RDF, RSS and JSON). \u00a0However, no keyword system is complete without a pretty tagcloud to go with it. \u00a0Implementing one requires us to create a whole new page (for the tagcloud itself) and a way to get to it from the main repository (in this case, a simple menu item).<\/p>\n<p>As I mentioned in my earlier post on the RedFeather architecture, pages are implemented within RedFeather using function pointers. \u00a0In this case, all we need to do is write a new function in the form &#8216;page_xxx&#8217; (which is then automatically accessible at index.html?page=xxx). \u00a0Page functions are used by RedFeather to output data or html to the web; this can be done either manually, or using the RedFeather templating system (which I talked about in great detail in a previous post). \u00a0The tagcloud is intended to be \u00a0normal page within the site, so it should use the template. \u00a0The system then accesses the global resource data and builds the tagcloud by writing html to the $BODY global variable; this is then written to user by calling &#8220;render_template&#8221;.<\/p>\n<figure id=\"attachment_943\" aria-describedby=\"caption-attachment-943\" style=\"width: 533px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.59.01.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-943\" src=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.59.01.png\" alt=\"\" width=\"533\" height=\"280\" srcset=\"https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.59.01.png 533w, https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-16.59.01-300x157.png 300w\" sizes=\"auto, (max-width: 533px) 100vw, 533px\" \/><\/a><figcaption id=\"caption-attachment-943\" class=\"wp-caption-text\">Example tag cloud<\/figcaption><\/figure>\n<p>Adding a menu item to allow users to access the tagcloud from the browse page is a case of simply writing a new function in the correct format. \u00a0This is then processed by the toolbar renderer and added to the menu.<\/p>\n<figure id=\"attachment_945\" aria-describedby=\"caption-attachment-945\" style=\"width: 557px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-17.08.07.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-945\" src=\"http:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-17.08.07.png\" alt=\"\" width=\"557\" height=\"222\" srcset=\"https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-17.08.07.png 557w, https:\/\/blog.soton.ac.uk\/oneshare\/files\/2012\/11\/Screen-Shot-2012-11-08-at-17.08.07-300x119.png 300w\" sizes=\"auto, (max-width: 557px) 100vw, 557px\" \/><\/a><figcaption id=\"caption-attachment-945\" class=\"wp-caption-text\">New menu item<\/figcaption><\/figure>\n<p>This consistent, concise architecture allows even intermediately skilled programmers to very quickly create exciting new plugins and features for RedFeather.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The last plugin I&#8217;m going to talk about during this initial period of RedFeather development is the keywords plugin. \u00a0While the ability to add keywords to a resource is\u00a0desirable\u00a0enough to be a candidate for inclusion in the RedFeather core itself, &hellip;<\/p>\n<p class=\"read-more\"> <a class=\"more-link\" href=\"https:\/\/blog.soton.ac.uk\/oneshare\/2012\/11\/08\/the-keywords-plugin\/\"> <span class=\"screen-reader-text\">The Keywords plugin<\/span> Read More &raquo;<\/a><\/p>\n","protected":false},"author":109,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[15431],"tags":[15415,15437,331,15416],"class_list":["post-898","post","type-post","status-publish","format-standard","hentry","category-redfeather-2","tag-redfeather","tag-oerri","tag-outputs","tag-ukoer"],"_links":{"self":[{"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/posts\/898","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/users\/109"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/comments?post=898"}],"version-history":[{"count":7,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/posts\/898\/revisions"}],"predecessor-version":[{"id":947,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/posts\/898\/revisions\/947"}],"wp:attachment":[{"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/media?parent=898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/categories?post=898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.soton.ac.uk\/oneshare\/wp-json\/wp\/v2\/tags?post=898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}