Site Building http://nerdstein.net/ en Drupal, Identity, and the Road Ahead http://nerdstein.net/drupal-identity-road-ahead <span class="field field--name-title field--type-string field--label-hidden">Drupal, Identity, and the Road Ahead</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>In the wake of the poor leadership demonstrated by the FSF, there was a sharp contrast displayed today at DrupalCon. Passion, interest, and innovation around open source software seems to be at an all-time high. One of the most prevailing open source projects, Drupal, just turned twenty years old. You can't help but recognize Dries and other long time community leaders for choosing open source before it became cool and helping to push the boundaries of what a large, open source community is capable of doing. Today's Driesnote was an inspiring message fitting for now and the road ahead.</p> <p>While twenty years is a celebratory milestone, what about the next twenty? Will Drupal be around to see it? It's of no surprise that Drupal faces competition in a number of forms. WordPress continues to see strong adoption. Proprietary tools, both headless and conventional CMS tools, continue to pump investment to compete with Drupal's largely volunteer led community. But, to their surprise, Drupal persists and, arguably, is stronger than ever. For there to be more certainty, Drupal needs to evolve and more clearly define it's identity.</p> <p>One may wonder why after twenty years that the identity of the project is in question. Well, simply put, this is because Drupal has evolved with time. In my opinion, it's solved key problems at the right time to remain relevant. While starting as one of the first database driven CMS systems, it moved into one of a no-code site building platform. In Drupal 6, 7, and 8, it then drove major new features and made major strides in it's developer framework. Drupal 9 continues more of an investment in decoupled API-based features and underlying dependency modernization. Today, Drupal needs to position itself through an identity and vision that helps sustain itself. Dries is taking the project back to its roots and for good reason.</p> <p>When interviewing Drupal candidates during my agency time, I would ask every candidate how they solved problems when creating Drupal applications. The best candidates responded with the same message. You would leverage both out-of-the-box and contributed Drupal features to build as much as possible before diving into code. I used to call it the 80/20 rule. 80% would be site building and 20% would be tweaking what was configured to match customer-specific requirements. As the Drupal framework matured, the community invested more features into core and other features were made in the contributed space. Modules were created that solved more problems or even opened up new, easier to use APIs to simplify the development. Now, outside of the specific visual branding and theming work, users readily can create a Drupal application that is even closer to 100%. Drupal remains fully qualified to be customized and extended through code, should you need it. Today, we find Drupal not only offering a robust development framework but it also has readily configurable features can compete with anything. It's time to get back to what put Drupal on the map: recommit to the experience of low/no-code tools that help a non-technical persona.</p> <p>Many Drupal community members came for this originally. Structured content could readily be modeled through CCK. Field types could readily be added through core-sponsored modules or extended with contrib options. Content displays could readily be created through View Modes or Views. Much of this possible without adding any code. This pathway helps non-coders get into code when they need more. Individuals readily understand the capabilities of the system and the potential problems solved before ever writing a line of code to customize it. Then, figuring out how to do this customization is a logical next step and one people can use specific experiences that extend their Drupal knowledge. These experiences and the corresponding code are often great things to contribute back. The community is fantastic and at the ready to help make these outcomes happen. This is the path many of us went down both using and contributing back to Drupal, one that inevitably grows the space of relevant first-hand problems we solve, and one that doesn't require a Comp Sci degree through it's organic growth.</p> <p>Dries demonstrated bravery and was refreshingly honest during his keynote. It can be difficult to stand in front of a large audience and admit where the project you've spent twenty years creating is falling short. He was forthcoming that the current experience of Drupal was too technically minded and that we needed to shift the tide to one of an easier, more native experience those in other open source communities are used to. He presented a Project Catalog feature as one of the more straightforward ideas that could help enhance Drupal. It's a cool ideal. Non-technical users could avoid Composer and work around some of the Drupal-specific jargon that can be a barrier of entry. An investment in the Drupal Association will be made to help continue to modernize and build tooling that aide in the experience of contributors through Gitlab, bots, and more. In a previous DrupalCon, the experience of installing Drupal was a major focus, too. I fundamentally believe this shift in focus can help Drupal become significantly more accessible and appealing to a wider audience. I believe this can open doors for more people to see the value in Drupal, help the community grow, and allow more people to have professional opportunities with Drupal.</p> <p>For those reasons, I left the keynote inspired and grateful for the chances I've had. Much like my motivation for simplytest.me, I want to open doors to see others have this same experience. I see the path forward now for Drupal itself and fundamentally believe Drupal is and will more readily be competing with Contentful, Wix, WordPress, SquareSpace, and all other enterprise, proprietary CMS systems at the same time. Drupal can and should be aggressive in positioning itself as the open source, low/no-code solution for all of the modern, backend digital systems. The features are there. The development extensibility and modern framework is there. The shift toward a site building, no-code focus will help Drupal shine. Let's dive in to help Drupal continue to be a leader for the next twenty years.</p> <p> </p></div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed, 04/14/2021 - 18:41</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/1" hreflang="en">Site Building</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Wed, 14 Apr 2021 22:41:04 +0000 admin 125 at http://nerdstein.net Custom REST Resources in Drupal 8 http://nerdstein.net/blog/custom-rest-resources-drupal-8 <span class="field field--name-title field--type-string field--label-hidden">Custom REST Resources in Drupal 8</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Introduction</span></span></h1> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Drupal 8 is a great platform for storing structured data and exposing web service endpoints. This offers Drupal a competitive advantage when creating a decoupled application or building Drupal as part of a larger enterprise of systems. Core offers many complementary out-of-the-box features to publish web services and configure them in different ways. This includes roles/permissions, Rest Web Services, Serialization, Views, and more. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Since Drupal is so robust out of the box, it often minimizes the need for custom development. But, Drupal’s framework has support for any customized needs. The following article describes how to create a custom REST endpoint in Drupal 8, as we share how to conditionally return a quote from a provided Arnold Schwarzenegger movie. </span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Development</span></span></h1> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Assuming you </span><a href="https://www.drupal.org/docs/8/creating-custom-modules"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">have a custom module already created</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and enabled, we can leverage the native autoloading features to register a new plugin class for a custom REST resource.</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Place a new </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">MyCustomResource.php</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> class in your module’s </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">src/Plugin/rest/resource</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> directory. Create any missing directories as needed. The REST resource will automatically get discovered after </span><a href="https://www.drupal.org/docs/user_guide/en/prevent-cache-clear.html"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">clearing the cache</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">.  </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 1: </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">modules/my_custom_module/src/Plugin/rest/resource/</span><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;"><a href="https://gist.github.com/nerdstein/9f5c71e40e6d073890cba9ea638d895b">MyCustomResource.php</a></span></span></p> <p> </p> <script src="https://gist.github.com/nerdstein/9f5c71e40e6d073890cba9ea638d895b.js"></script><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">This example demonstrates an exposed GET endpoint found at the path </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/api/custom/arnold</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. Let’s walk through the code to get an understanding.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Namespacing</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">If the PHP class is placed into a module’s </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">src/Plugin/rest/resource</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> directory, Drupal will automatically register the class and knows, given the path, to associate it with a REST resource. This is a common pattern leveraged by Drupal’s Plugin API.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Annotation</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Annotations define static attributes and configuration of the REST resource. Lines 16-27 demonstrate the annotation for the REST resource class. Most notably, a unique ID (no spaces and use of underscores) is shown in line 20 and a human recognizable label on line 21. Lines 23 and 24 define the endpoint for the API, for which requests can be made.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Base Class</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Classes are capable of implementing interfaces (a common, shared definition of expected functions) or extending base classes that have defined functions. Line 28 demonstrates the name of the custom class, which should match the filename per object oriented best practice. Extending </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">ResourceBase</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> as a base class allows us to inherit standard REST resource functionality and only needing to develop our specific functionality within our class.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Dependency Injection</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The use of an overridden </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">__construct</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and the complementary </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">create</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> functions demonstrate how we use dependency injection to load additional system resources into our class. In this example, I load serialization formats, a specific logger, the current user, and the current request from Drupal’s runtime container. The request and current user extend the constructor of the </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">ResourceBase</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> class and saved into the </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">currentUser</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">request</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> attributes of a class instance defined in lines 35 and 42 respectively. This demonstrates how to extend your REST resource for any additional class attributes.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Request Method</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Web service endpoints define the expected methods of the request. Drupal’s REST resource plugins support requests by defining specific methods within the class. Line 99 demonstrates a </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">get()</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> method that corresponds to a GET request available from the endpoint. All code within this method is used to generate the response. </span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Caching</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Responses can be cached from the endpoint to help with performance. Lines 117-121 demonstrate specific caching directives that can be used at your discretion. In this example, I’ve set it to not cache requests.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Response</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Line 124 demonstrates the response triggered by returning a ResourceResponse object with the desired data.</span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Configuration</span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Once REST resources are developed, they still require configuration. Start by downloading and enabling the </span><a href="https://www.drupal.org/project/restui"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">REST UI module</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> to manage Drupal’s REST endpoints at </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/admin/config/services/rest</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">.  You should see your new REST resource defined in the list. </span></span></p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"> </p> <p><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-1.png" style="width: 650px; height: 346px; border-width: 4px; border-style: solid;" /></p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The REST resource endpoint will be activated once enabled and configured. Configuration includes selection of the exposed methods (from those available from your class), the authentication (select cookie to use Drupal’s default authentication tied to it’s login), and the serialization formats (JSON is commonly used for tools like React and Vue).</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Finally, you need to define who is able to access the endpoint. </span></span></p> <ol style="margin-top:0pt;margin-bottom:0pt;"><li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Go to Drupal’s permissions page, found at </span><span style="font-size: 11pt; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/admin/people/permissions</span><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">, </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Search for the permission tied to your REST resource, e.g. “access {METHOD} on {RESOURCE NAME} resource”, where method corresponds to the method defined in your class, like GET/POST/DELETE/PUT, and the resource name is the </span><span style="font-size: 11pt; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">label</span><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> in your class annotation. </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Select the applicable roles that you wish to access (which will be verified by your authentication method). Selecting anonymous will allow your endpoint to be publicly accessible.</span></span></p> </li> </ol><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">An Extended Example </span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The previous example outlined the basics of Drupal’s REST resource API but did not describe how to leverage other Drupal features. I’ve created a second example that is more relevant for retrieving Drupal-sponsored content. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 2: </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">modules/my_custom_module/src/Plugin/rest/resource/</span><a href="https://gist.github.com/nerdstein/417c8ef105205d8159386a26c771ee9b"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">MyCustomResourceRandom.php</span></a></span></p> <p> </p> <script src="https://gist.github.com/nerdstein/417c8ef105205d8159386a26c771ee9b.js"></script><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The updated example assumes the use of an “arnold_quotes” content type, which leverages the “title” field for the quote and has a boolean “bonus” field to tag specific quotes as a bonus feature. It also assumes the use of a custom permission for accessing bonus quotes, which can be assigned to a user by role. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The example leverages an </span><a href="https://api.drupal.org/api/drupal/core!lib!Drupal.php/function/Drupal%3A%3AentityQuery/8.2.x"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">entity query</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> to conditionally load quotes to first filter by a movie parameter and bonus products, if the user has the correct permission. A random returned node is loaded and the quote is returned through the response.</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 2 better exemplifies a use case for a custom REST resource in Drupal 8 and potential opportunities to interact with the other Drupal subsystems.</span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Testing </span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Intuitively, you may go to a web browser to test your endpoint. But, web service endpoints are configured to only accept requests in specific serialized formats and methods. A standard web browser request likely won’t create a request that matches your endpoint settings, but Drupal will still return return a result that likely prints an error and is in the serialized format you selected (e.g. JSON).</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The easiest way to test your endpoint is through a tool like </span><a href="https://www.getpostman.com/"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">Postman</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. After downloading, configure a request to your endpoint.</span></span></p> <p> </p> <ol style="margin-top:0pt;margin-bottom:0pt;"><li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Specify the method (e.g. GET)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Specify the full URL to the endpoint (e.g. <a href="https://mysite.com/api/custom/arnold">https://mysite.com/api/custom/arnold</a>)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Add a parameter for “_format” and specify the desired response serialization format from one configured by the endpoint (e.g. “_format=json”)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Add any other parameters for the endpoint (e.g. “movie=Predator”)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Set a header for the correct mime type by setting “Content-Type” to “application/json” to match expected request serialization formats</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">If authenticated, click “Cookies” and enter a cookie value for your Drupal site after logging in (you can get this from your browser)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Click “Save” your Postman settings to persistently store </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Click “Send” to test the request</span></span></p> </li> </ol><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-2.png" style="width: 550px; height: 244px; border-width: 4px; border-style: solid;" /></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-3.png" style="width: 550px; height: 188px; border-width: 4px; border-style: solid;" /></p> <p> </p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Further Reading</span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">More details can be found on the </span><a href="https://www.drupal.org/docs/8/api/restful-web-services-api/custom-rest-resources"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">official drupal.org documentation</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. And, just as I was finishing the article, Open Sense Labs published </span><a href="https://opensenselabs.com/blog/tech/creating-custom-rest-resource-get-method-drupal-8"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">a similar article</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> that is both helpful and explanatory.</span></span></p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Sun, 12/16/2018 - 18:46</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/1" hreflang="en">Site Building</a></div> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Sun, 16 Dec 2018 23:46:38 +0000 admin 109 at http://nerdstein.net Salary and Reappointments http://nerdstein.net/reappoinment-salary-project <span class="field field--name-title field--type-string field--label-hidden">Salary and Reappointments</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>I set up a system in Drupal that imported security-sensitive data such that members of the organization could update appointment and salary data for their departmental staff. I tried to limit the custom development used in this project and focus on site-building efforts. The principle goal was to ensure access control, encrypt the data stored, and emphasize usability to limit data entry mistakes. This project was developed under a tight deadline and with limited assistance.</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue, 09/03/2013 - 22:16</span> <div class="field field--name-field-image field--type-image field--label-hidden field__items"> <div class="field__item"> <img loading="lazy" src="/sites/default/files/HR_System.png" width="817" height="431" alt="" typeof="foaf:Image" /> </div> </div> <div class="clearfix text-formatted field field--name-field-features field--type-text-long field--label-above"> <div class="field__label">Features</div> <div class="field__items"> <div class="field__item"><p><strong>Security</strong><br /> We leveraged the encrypt and field encrypt modules to store the fields of nodes in an encrypted fashion. We set up a system settings form that quickly changed the behaviors of the system to turn on/off specific features or lock the entire system down. We set up a custom role and taxonomy based access control system that limited access to nodes based on the shared taxonomy and capabilities of the role. We created a custom drushrc.php file to limit Drush commands from being executed by unauthorized users. We altered node forms based on the user's capabilities and the enabled features of the system. And, we used SSL and federated login for user authentication.</p> </div> <div class="field__item"><p><strong>Front-End / Sub-Theming</strong><br /> We leveraged jQuery and drupal_add_js to lock down visible form data and to perform standard calculations for usability. This enabled us to build in business logic and create an extremely easy to use data collection experience. A standard base theme was forked as a sub-theme and overridden to make minor cosmetic changes.</p> </div> <div class="field__item"><p><strong>Site Building /  Feeds / Views</strong><br /> We set up custom content types to perform the data collection. We leveraged modules like Field Collections and Entity API to create a list of composite entities inside of the content type. We leveraged feeds to perform the XML-based import from a secondary data source. For security purposes, we patched the Feeds module to delete the source data file after import. I leveraged Views and Views Bonus Pack to output the same XML file such that the system can be cleared out and the data can be easily restored.</p> </div> <div class="field__item"><p><strong>Development Workflow</strong><br /> We leveraged Features and many complimentary modules to make the content types, feed importers, and field collections exportable.</p> </div> </div> </div> <section class="field field--name-comment-node-portfolio field--type-comment field--label-hidden comment-wrapper"> </section> Wed, 04 Sep 2013 02:16:14 +0000 admin 2 at http://nerdstein.net http://nerdstein.net/reappoinment-salary-project#comments World Campus http://nerdstein.net/world-campus-project <span class="field field--name-title field--type-string field--label-hidden">World Campus</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>I served as the technical project manager with peers responsible for strategy and administrative duties. This project was my first large-scale Drupal website with the need to support high volume traffic. My responsibilities were:</p> <ol><li> Work with the strategy team to define the specifications for the site</li> <li> Developed the initial mobile site in jQuery mobile</li> <li> Create a new development workflow and train others to use code versioning, continuous integration tools, and local development</li> <li> Develop several Drupal modules that interfaced with third party APIs for the questions database and course catalog</li> <li> Develop the Request for Information form and workflow</li> <li> Work with an outside vendor to help build the site</li> <li> Work with other peers to help build the server infrastructure </li> </ol></div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue, 09/03/2013 - 21:32</span> <div class="field field--name-field-image field--type-image field--label-hidden field__items"> <div class="field__item"> <img loading="lazy" src="/sites/default/files/Penn_State___Online_Degrees__Online_Courses__and_Online_Certificates_offered_by_Penn_State.jpeg" width="1031" height="636" alt="" typeof="foaf:Image" /> </div> </div> <div class="field field--name-field-portfolio-link field--type-string field--label-above"> <div class="field__label">Link</div> <div class="field__item">http://www.worldcampus.psu.edu/</div> </div> <div class="clearfix text-formatted field field--name-field-features field--type-text-long field--label-above"> <div class="field__label">Features</div> <div class="field__items"> <div class="field__item"><p><strong>Development Workflow</strong><br /> We set up a development workflow that used Mercurial and Jenkins to push code between local, development, demo, and production systems. Jenkins scripts were developed in Fabric, based on Python. These scripts strongly leveraged Drush commands and SSH.</p> </div> <div class="field__item"><p><strong>Systems Integration</strong><br /> We leveraged third party web-service APIs to retrieve XML for the knowledge base and course catalog. These were custom developed as Drupal modules and using the Block API and Context to place the blocks appropriately.</p> </div> <div class="field__item"><p><strong>Request for Information (RFI) Form</strong><br /> We developed a data collection form with the Form API. We developed a custom interface to our CRM system that populated drop-down field values of the form and then stored the data within the CRM system. </p> </div> <div class="field__item"><p><strong>Infrastructure</strong><br /> We worked with our peers in the infrastructure team to set up a Varnish/Memcache based solution that was blazing fast.</p> </div> <div class="field__item"><p><strong>World Campus Mobile</strong><br /> We used Views Bonus Pack to output an XML-based feed of node data used in the mobile solution. Not all site nodes were to be rendered on the mobile site, so we created a custom field in the content types to supply this filter. We developed a simple PHP system that synchronized the feed from Drupal and set up a menu hierarchy specific to the mobile site. The front-end mobile solution was based on jQuery mobile.</p> </div> </div> </div> <section class="field field--name-comment-node-portfolio field--type-comment field--label-hidden comment-wrapper"> </section> Wed, 04 Sep 2013 01:32:13 +0000 admin 1 at http://nerdstein.net http://nerdstein.net/world-campus-project#comments