How to Build Beautiful Forms in Drupal 7

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
good morning everybody I'm glad you're here and I'm really excited I got to do one of the first sessions because like by day two my voice is so hoarse wouldn't be able to hear me anymore so okay welcome to how to build beautiful forms in Drupal 7 and just to kind of clarify what this is about we're tackling some of the dynamic parts of the form API this is kind of a coding based thing we're not looking at CSS really or any ways to kind of prettify the form but really actually making a form usable by dynamically changing it as it gets filled out okay no one's getting up and leaving so good so um just in case you guys want to take a moment to write these down a couple of resources I do a site called buildamodule comm um can I see a raise of hands from anybody who's watch videos on there okay just a few um I have an hour and a half section on the form API so if you want to review kind of what we talked about today later you can follow this link and watch those for free and then if you want to download the source code we're going to be moving kind of fast so it'll be a little difficult to follow along with the source code today but if you want to as you go through this later you can download it at this second link here haha okay that's one argument for not putting music on the site um and then if you want to give feedback afterwards there's a link down here at the bottom and I'll go ahead and show this page at the very end um just real quick as you guys are writing this down I wanted to share it a little anecdote about context so on the site on the build a module site when people watch a video there's a little forum afterwards that says hey do you have anything you know you want to share about this like did you like it did you not like it or whatever and a lot of people you know right just simple like good or not so good or whatever you know really simple things and every once in a while I get something that's just sort of strikes me and the other day I got one that just made me laugh out loud and said my girlfriend learned absolutely nothing from this and I was thinking about okay what's the context here is someone getting forced to watch these like tutorial videos on coding she's like oh yeah let's hang out watch something okay you know so like that was the wrong context for her and that so I want to make sure you guys are in the right context I don't want you guys to be the girlfriend in this case so let me get a sense real quick of how many people in here have built a simple module or feel like you could probably if you need to do okay and how many people have kind of touched the form API in some way altered a form built to form something like that okay so probably bout a third or you know I'm on the counting for maybe twenty percent of people whose arms aren't like awake yet so what we're doing here is we're actually leaping kind of fast-forwarding a bit through learning the form API through some of the quicker aspects of or some of the basic aspects of building forms so I'm assuming that in it's okay if you haven't yet but I'm assuming as we start here that you've built a form you know how to embed it on a page you've worked with validation functions with forms and submission functions so if you haven't done this and a lot of you haven't that's okay you'll just have to kind of infer from what we do sort of what's going on there and you can always rewind later to watch the videos or you know review the documentation on ddoddo about the form API and you know afterwards to kind of regroup so what we will be covering is the state attribute which allows us to toggle certain sections of a form on and off it is new in Drupal 7 we'll look at how to use hook form alter will use hook form alter to add an autocomplete and then we'll use the Ajax in order to dynamically add new inputs to a form so state and Ajax are kind of similar and you'll see that but they have some important differences yes this is Drupal 7 stuff yes okay so if you have any questions if you like get sort of stuck or whatever you can either wait until the next section what kind of you know sort of start start again or you can you know raise your hand just let me know I'll try to bring you back up to speed I guess we should probably close these throws thank you okay so I'm let me refresh this and my full screen was a little different size earlier so I have a vanilla Drupal seven side I've just installed it no contributed modules or anything I have downloaded that resource pack that you saw the link to and this includes a folder called input and yeah my mouse is there let's bring that back hmmm okay there you go so the folder is called input and this is actually a module demonstration module and so what I've done is moved it over to my sites all modules folder which is where you put contributed modules inside of the folder is a steps folder and this contains snapshots of the code as we go through it now like I said I'm kind of assuming we've gone through building a simple mod simple form adding submission function and a validation function and looking at some of the attributes of the form API some of the attributes of elements and different types of elements that sounds strange to you no problem but this is kind of where we're starting off and so we've built a couple of examples in the source code and we're going from here okay so I'm opening up one of these states or one of these snapshots for the state attribute it's number six if you follow along later and I'm just going to copy the code and I'm going to paste it over the code that's in the input module file which is empty right now so this is again the code that you downloaded so this is so I don't have to type out the code as you guys are watching so I'm going to jump to the browser and in order to whenever you install a module installing a module we usually clear your cache for you but when you update a module you need to clear the caches manually and you can do so by going to configuration and then going to performance and then going to clear all caches and what this will do is clear the menu registry so if there's new pages that have been added with your module it will add them so you can now visit those pages and a couple other things too so there will be another reason why we need to clear the cache later I'm going to go ahead and refresh the page and it will add this form API examples link in the navigation I'm going to click it and there's a more elements and attributes link here and I'm going to click that okay so this is the form that we're going to be working with it's really simple so just to demonstrate the state attribute in the Ajax attribute and the idea here is that it's asking if you own a computer and if you select yes I'm going to go ahead and do that then it drops down this field set here with a couple additional inputs so what's happening here and if I click no it will hide it so what we're doing is we've added some elements to our form and we're dynamically showing it and hiding it with the state attribute now if you've used how many people here feel comfortable enough with jQuery or JavaScript that you would be able to do this in JavaScript Wow okay cool so you kind of know what's going on here let's go ahead and look at how you would do this in the form API and just in case you're not aware the reason why it's good to do this in the form API is because it gets run through several altar hooks which allow other modules to adjust the form later so if they wanted to show additional inputs when this yes got clicked they could do that if they wanted to change the behavior they could do that too okay so I'm going to just start out here at the very top of the code we have a hook menu function which allows us to register pages and we're on this input examples more page we're using the callback for this page of Drupal get form which will take a form render array and convert it into HTML how many people here know a render arrey is okay if you've ever worked with PHP you've you've probably run into the pattern where you boil down a set of settings or configuration options and use that in order to kind of in loop through it in order to render some HTML or or go through some output so render array is kind of those settings boiled down into a multi-level array inside of PHP and you will see what this looks like in just a second the argument that we take here is the function that returns the render array for the form so what I'm going to do is go ahead and search for this on the page it's the input more form function and so we'll just take jump me to the place in the code where this form array is and inside of here we're defining a form render array we're starting out with this yes or no radio button that allows you to select the if you have a computer or not this is pretty vanilla and you can see we're just adding elements to the form array here and then the field set is a special type of element here you can see that the type here is field set and we're giving it a name of computer stats and here's where we're adding this States attribute now the state's attribute it affects this element but also any embedded elements to any elements that are children of this element so here we have the thickness element which is part of the computer stats it's inside of computer stats you can see here in the array structure and so all of these will get shown or hidden so here's the state's attribute and let me just read this in plain English it says that this element will be visible if an input with the name of computer has a value of yes which seems pretty straightforward let me take you through the through the pieces here first of all if you've done this with jQuery or JavaScript you probably a little you may be kind of flipped around because you would associate the action you know that yes or no click you would associate the action with that input you'd say when this is clicked then we're going to show this but in the form API it switched round you add the state to the element that's going to be affected rather than the element that's doing the effecting so this state's attribute is an array and so it contains what could be a list of multiple states in this case we're just adding one state for visible but we could also add a state for visible for invisible as well and then this next part is an array of conditions that need to be met in order to make this state occur the first part of the condition is the jQuery selector for the element that's going to trigger that we're going to check the value for or that we're going to check the state of so in this case what we're doing is looking for an input that has a name and that's the attribute of the input of computer and we can always rely on this because this when we add an element to our form array and we give it this key here a computer that will be the name attribute in the form so we can always use this name attribute in the code here and so we're checking to see if this element has the value of yes okay so let me just jump back and that's really it it's just these you know three lines really and once these three lines are run inside of the form API then that creates that ability to expand and contract that option those options on the form so on this page yes or no we're showing it and hiding it okay any questions so far yes well that Selectric with that did you have used any other jQuery selector that specifically no you can use any jQuery selector there the input name is one you can rely on so you know but if you know that an element on the page has a certain class for example or an ID you could use that as the selector anything yeah yeah basically like align it with something outside of the form yeah yeah totally mm-hmm so like if you want like part of the form to display of somebody's you know if there is something that exists like in there or profile or something and you want that just to show up automatically you could do that yeah it's kind of nice yes anyone want to sit on this okay any other questions okay so um so this is very similar to what we're going to do with the Ajax attribute but the difference is that with the state attribute all of these form elements are part of the form of reading we're just showing and hiding it with Ajax what we're doing is building new inputs into the form behind the scenes okay but we'll get to that in just a sec we're going to do a little segue here to explore the form hook form alter okay so we have a form here for the search this is the search form just enabled by default in Drupal and what we're going to do is take this input and convert it into an autocomplete that keeps track of people's search history so I type in something and it saves it to history and the next time I start typing it in it will drop down an option to allow me to like pre fill that input with my search history so you know kind of google suggestions style right so I'm going to go back to the code and again we have this snapshots folder called steps inside of the the module directory and I'm going to open up the 7th step which is called hook form alter and again this is just a snapshot that includes some additional code from the previous snapshot and and I'm going to go ahead and save it okay so the only code that I've added here is this form alter function and then this input search submit function so what we're doing is adding a new submission function to this form on the to the search form and the idea of submission functions is that they can stack one on top of another so we have a certain amount of processing that's going to go on because of the existing submission functions for the search form but we're going to tag on some additional stuff we don't want to edit it or manipulate it in any way we just want to tag on some stuff so way we're going to do that is by using a hook form alter function it takes three parameters the first which is the form which is the form render array that gets returned from the function that we were just looking at form state which contains the values of the form once it's been submitted so we can then access whatever content the user is filled in and then the form ID which is a unique ID for the form now a hook form alter function will get called on every form on the page so we need to target the specific form that we want to alter and so we need to do that we need to know the form ID the way we can find that is by going to the browser and in Firefox I'm using a plug-in called Firebug which most of you are probably familiar with but you can also use other sort of development tools in any of the browsers but what I'm going to do is right click on the element here and click inspect element and this will bring up the Firebug dialog and this will highlight the input that for the search form and what I'm going to do is actually move up up the hierarchy this is the source code for the page until I get to the form that contains this input and the ID here is the ID that we're looking for the difference is that in in the ID here that we need to convert the hyphens the underscores so instead of search - block - form it will be search underscore block underscore form and this is also the name of the function that gets called for the form render array for this form so if you're looking around this is how you'd find it okay back to the code yes yeah well this is just a you know the only thing that we're doing is trying to find what our form ID is we could do so by looking for the name of the function that creates that that form and that would work just fine but if we're looking for something quick we're just looking for the source code so we don't have to do the only thing we're doing here is just aligning those that difference the hyphens right with the change that we need to make in that code you'll see this sort of switch between sorry if that was confusing you'll see the switch between hyphens and then rapport is quite a bit but it's all conventional so you know when that's happening but this is I think the only point where we're going to see that in this except it might happen with the the form input name as well I don't did that help at all ok can you clarify just a little bit what your question was but you're you're stating that there's a conventional sometimes turn to ashes and others write up wondering ok in this case what's happening is that the form name or the the function name is when it gets rendered into HTML behind the scenes you don't have to worry about that those those underscores get converted to hyphens I don't know why um page 4 you guys do but functions can't include hyphens or functions can't but I don't know why they're converting it to hyphens in the ID yeah well I think it's also because CSS and use underscores it can and IDs IDs typically contain underscores rather than hyphens anyways but um yeah so I guess this is just a rule of thumb it's a Drupal convention right okay that's the way I'm going to answer every question for now it's a Drupal convention okay let's move on okay so um so I'm using a switch statement here to you know add this logic only to this particular case but you could use an if statement here too we're checking for the ID and we're saying we're adding its submission function by adding it to the submit attribute and when I say attribute you know it's a Matthew when there's this hash tag here in front of it and we're just adding it to the end of the submit array and so this is the function that we need to create in order to add our logic so if we look down here here's our function name a submission function takes two parameters form and formed state again these are the same as what was used in form alter our step is getting the search phrase that's being used and we're doing that by looking in the values array that's inside a form state now again this is the form values that have been submitted and we're looking for the search block form input now the way we get this name if we jump back to the browser again we have this input right here highlighted in the source code and Firebug down here and if we look if we scroll over a little bit to where we see the name we see search and these are underscores search underscore block underscore form and so if you want to target a specific value without using a debugger or using de valor' something like that this is a quick way you can find it and target it in your code I'm going to jump back to the code okay so that's how we're doing here grabbing that search phase and then what we're going to do is use a simple mechanism for storing the search history and getting it back out using the Drupal variable table and the two functions that we're going to use our variable get and variable set they're pretty straightforward the first part is pulling out the variable if it exists already because we're you know this will happen several times so we want to grab the search history first and so we're getting it using variable get if the variable doesn't exist already we're going to set it as an empty array now this isn't necessarily form API specific but this is just some code so you can kind of understand you know this example a little further we're going to add the search phrase that we just pulled out of the form to the end of the search history array and then we're going to reset the variable which now includes the new search history item then we're going to set a message that says your search phrase has been saved to history so someone knows we're spying on them so let's go ahead and take a look at what this looks like in the code when I close out fire bug and I'm going to clear the caches because when you add a form alter function or submission function in this case you need to clear the caches I'm going to refresh the page okay and I'm going to do a search for test right here in the search box when I do we get this message here that says your search phase has been saved to history if we do this again and just do another test then we get that message again okay so now we have two items in the search history I actually have a few more because I've been running this test a few times so now the step the next step is to add the autocomplete to this input so autocomplete is kind of fun but there's a few steps involved so I'm going to jump back to the code and I'm going to pull up the eighth step which is called autocomplete in this snapshots folder I'm going to copy the code and paste it over the module file I'm going to save it okay I'm going to scroll up to our form alter function so this is where we ended from our previous step we added the submission function in this step what we're doing is adding an autocomplete path to an existing element so that text text element that we saw we want to add this autocomplete path attribute to what this the value of this is going to be a path a Drupal path so we'll need to create this this is just kind of an arbitrary path but we'll need to add this page so this is this is it in order to add just that the little loader you see on autocompletes in Drupal this is pretty much all you need right here the next step though is to create this page this input /search autocomplete so I'm going to scroll up to the top go ahead and and I've added the item in our hook menu function here input slash search' autocomplete this is just a type of menu call back so we don't register any menu items for this page and the page callback is the name of the function that's going to get called that will basically return an array of matches for this autocomplete so I'm just going to do a search for this function so again we're just naming this function whatever but I've already created it so I'm going to jump to the code and so here's our this is what's needed in order to get an autocomplete to work basically what we're going to do is grab the search history we're going to loop through the search history and see if we can find the phrase that got searched for inside of each item if there's a match we're using just the STR ist our PHP function to match it if there's a match then we're going to add it to this matches array and then the trick for for this autocomplete is that we're going to use Drupal JSON output which is just a tuple function that will take this array and kind of package it up serialize it in a way that can be used on the JavaScript side so JSON is short for JavaScript object notation and it's just a JavaScript object and then Drupal takes over from there so this is pretty simple code but you need these specific steps in order to do this one kind of interesting part right here is that the array that we passed a Drupal JSON output the key here is the value that gets put into the textbox when the drop down element is clicked and then the value here is what you see in that drop down so they don't have to be the same thing so our drop down is going to be very simple it's just going to be some text but we could have images in it any HTML rich text whatever so you could have previews in that drop down and when somebody clicks it then it just inputs an ID or you know some plain text inside of the input for now we're just using the same we're using phrase as both the key and the value here okay I'm going to save it and jump back to the browser I'm going to clear our caches again in order to register the new menu item for the autocomplete I'm going to refresh the page now you see this little circle here which indicates this is an autocomplete and if we start doing a search for test then we'll see this drop down here and we can select one and it will pre-fill this input okay any questions yes so you literally going on for any client so yeah yeah no JavaScript right it's all PHP and then cool yes yeah Drupal is designing way to search that array so with the positive character right right right so like you know if we're if we start typing in here and I'm continually typing it's not going to go and do the search you know because that just wouldn't be performant right so it waits for a little bit of a pause and then you see that little loading graphic so yeah that's kind of tricky to do if you've tried to do it in JavaScript it's not fun but but yeah it takes care of all that how many people feel like they're kind of on board and following along how many people feel like they're not really and they're kind of lost okay we're good if you're lost it's important to know that you're lost I think so hopefully hopefully you can catch up later like I said we're jumping kind of in the middle and I think these these particular things are recovering are tricky they're like the trickiest parts of the form API because there's several steps involved you're not just like turning something on turning something on building a menu item returning it in a certain sum code in a certain way so these are tricky and it took me a while to wrap my mind around it even after looking at good solid examples okay so the last thing we're going to look at is Ajax loading so I'm going to load up the code here it's the ninth step and I'm going to paste it over the module file that we've been working in input dot module I'm going to save it I'm going to go ahead and demo what this does before we go through the code I'm going to clear all the caches first because we're adding a menu item okay I'm going to go back to our original form by going to form API examples more elements and attributes I'm going to click yes here so this is well you've added this operating system in this computer information form and if I select an option here Linux in this case it adds this check box it says are you sure you're using and then the name of the operating system I don't know why you'd want to verify this but you know this it's a little contrived but the idea here is we're reacting based on the value of this so this says Linux here we chose Linux here and if we chose something else it would say that something else so if you if you're you know you if you work with JavaScript you know how to do this on the front end but this is all backend stuff this is this is the form being rebuilt in the back and using the values that are in the input so a good use case for this is that sometimes you don't know what elements should belong in a form at the beginning right like for example this is a form for a single computer but say we wanted to add this button at the bottom that says add a new computer and somebody can keep adding a list of computers as they go we don't know how many computers they have or how many they would want to register so we need to and we don't want to just build like a hundred of these elements in the backend and just show or hide them based on you know whatever criteria are being used so we want to just dynamically add these new inputs to the form again Drupal works a little differently than if you're just working on the JavaScript side or jQuery side you know probably that you can add inputs to a form on the front end you can just say you know we click this we'll just replicate this entire HTML and plug it in below and you know to the user well you know they just see this form expanding but to Drupal Drupal takes a snapshot of the form in the in the M in in in a cache and when the form gets submitted it checks this cache to make sure that the inputs that are being submitted are identical to the ones that are in the form like basically if there's any additional inputs that you've added with JavaScript they're not going to be part of that form state array that we can need this you can get to it with post you know but by using form state is nice so so with Drupal we have to rebuild the form to include any new inputs that we're adding so let's go ahead and take a look at what that actually looks like let me go back to the code and I'm scrolling up to our form render array called input more form this function here and what we've added is down here at the bottom so here's our operating system put it's a select box for the options we're using a function called Drupal mapa so switch is kind of a nice little handy function that will take a flat array and make it associative so you can use it with this options attribute and then we're adding this Ajax attribute here it takes two parameters at least this example does call back and wrapper what's happening here is that when this input changes this function is called right here that function then returns a render array or HTML that then gets plugged into a wrapper with this ID so an element some HTML element probably a div with this ID and so so this is the process that's going on and then Drupal rebuilds its cache the form API the form the forms cache gets rebuilt and so now we have some new inputs in our form and the user hasn't had to refresh the page or anything so so we have a couple of things that we have to do here when we need to define this function and second we need to add this wrapper somewhere in our form in order for it to get filled in so here's here's the first part of it we're adding our div with the ID of input OS verify wrapper here this is the we're just adding a new input of a markup type which is just some HTML this is called OS verify and we're adding the div as a prefix and a suffix so this is the opening and then the closing div if you've used the form API before you know that you could use a value here for the markup but there's a reason why we're using pre fit since prefix and suffix instead of using the value attribute and that is that when the form gets rebuilt where we going to we're going to repurpose this input and we're going to make it a check box instead of markup so this is kind of a cool thing about the form API is that you can actually dynamically change the element type so this is just some HTML but we're changing it here to be a checkbox and we're adding a title to it that says are you sure you're using and then the operating system so when this gets rebuilt we'll be checking the form state to see if somebody has selected an operating system if they have then we're going to go ahead and change this input here to a check box if they haven't we'll just leave it at what it is which is that markup but this prefix and suffix attribute will cascade into this because we've already set this form attribute we've already set this form element and these attributes are just cascading down to this OS verify element that we're adjusting here what's happening is that Ajax replaces this this wrapper here it doesn't just add something inside of it but it actually replaces it so if we want this Ajax interaction to work again we need to add this wrapper back as part of the as part of the form API as part of the Ajax call back now yeah okay yeah okay and then so the last part here is to take this callback which again we need to return some HTML from and create it the the Ajax callback takes two parameters form and form state just like a lot of the other functions that we've seen here and what we're doing is just returning the OS verify element inside of this form render array now this isn't the HTML but Drupal will take care of rendering it into HTML once we have just the render way render array we can return that and so this will then get put into this wrapper okay I'm going to jump back to the browser so we can see it in action and actually look at the code a little bit I'm going to refresh the page I'm going to inspect this element in Firebug so we can kind of take a look at what's happening in the source code here so the first time this form gets built we have this empty div right here input OS verify wrapper this is right below our select box you can kind of see the elements being highlighted that I'm hovering over in the main body of the browser and when go ahead and watch this element when we change the operating system so you see now it's no longer empty there's something in it we can expand it and if we expand it we'll see the check box input so Drupal goes back so so let's go ahead and go through the steps just real quick and then I'll see if you guys have questions I'm sure you do so the first part is adding our Ajax attribute to an element when this changes then this function gets called which returns a render array that fills in this element we add this element to the form as some markup and then when the user changes this input that gets pulled in and the form gets rebuilt in the backend okay questions about Ajax the Ajax attribute yes any particular part you wanted to see this is I guess this is the bulk of it right so when this form gets built initially I'm scrolling up to the top here the the render array gets passed to two parameters form and form state the first time that's gets rendered there's form state doesn't have any values right because the form is just getting rendered but when the form gets submitted or we're rebuilding the form of Ajax then it has some values in it so the first time this form gets built there's not going to be any value for the OS because the OS if we look at the browser is it defaults to empty it defaults to this right and so so there's nothing here so this code won't get run but once somebody selects it then there will be a value here and we'll go ahead and convert it to a check box how many do people feel like they kind of get the autocomplete stuff here yeah not the autocomplete with Ajax stuff if you wanted to that wrapper there for that uh checkbox getting selected not because you just did right I'm trying to figure out what happened there say that again value was set so you were to say is not oh right so if like this like or a little petri function not not empty yeah just give right got it and so let's go ahead and try that see if that does the trick yeah good call guys yeah yeah applause balls I knew that yes that's gay um yeah it's pretty different um I've only tried a couple of times the sex so uh so yeah it is it is kind of a different piece of the novel whole bunch of arenas yes it's kind of nice look autocomplete is the same but ajax ajax is very different yeah they've simplified it so much it's it's a pleasure to use now yeah sweet component um with this because if I don't I'm mentally the same tools you'd used to do like a multi stats one um or the regiment well multi-step is a little different yeah I mean you could probably build a multi-step this way I mean in like dynamically show like different parts of the form like if it's all one page but if you're going to be like submitting the form and then moving on to the next step then there's a different process for that yeah step you know movies that form right yeah yeah within a single step and you could like use state to like hide one section and show in the next section right those that kind of thing but then going back and forth you would need to like kind of trigger like back forth buttons and you know I mean it multi-step isn't like trivial you know with if you're using just you know vanilla form API stuff well yeah yeah when you going to do in a form that has an AJAX all back in if this would form alter if you call the game yeah yeah like any time a form as rendered hook for mulcher gets called is it being undergoing it is because the the the form is being rebuilt in the background you know you might not see any changes if we're not pulling the HTML to back to the front end but the cached version of the form may change in the background that makes sense for Ajax uh for this one we we didn't we just added the callback in the wrapper yeah um yeah that's a good question we can look it we can look in Firebug and see real quick so we changed that and it's going to system slash Ajax as the page so that takes care of the processing yeah um have you noticed any issues from the servers or some connections with Ajax yeah well I mean what you're doing is loading a page in the background I don't know if it goes through the entire bootstrapping process for Ajax I think it I think it must I think it must have to so there's that happening you know you get some additional performance because you're using Ajax and not having to do a full page refresh but as far as the server load it's going to be the same as any other page request so so like building in mechanisms to make sure that like you know like you know ten requests don't go out really fast it could be very helpful you know if you're if you're wondering about that but yeah a Jackson in this case it's just the same as any other page load yes yeah um it's a well the way we did it here was with a variable get and variable set and that just uses it what Drupal does is it stores it in a table called variables and this is that yeah this would have the same history for everybody so this example I mean you probably wouldn't want to put something like this in variable the variable table just as a disclaimer guys you know if you're gonna do something like this you probably want some other table to do it because variable gets the variable table gets loaded on every page low so you know you know even if you're not using those variables that gets loaded so if you were going to like actually build a robust version of this you probably want something else I think I'm out of time this ends at 11:15 I think so um thank you everybody so much it was really great
Info
Channel: BuildAModule
Views: 136,376
Rating: 4.762712 out of 5
Keywords: drupal, api, development, web, beautiful, d7, learn, how, to, howto, colorado, drupalcamp, event, tech, education, technology, tutorial, educational, software, school, lessons, student, students, free, teacher, teachers
Id: syqsH2CEu6U
Channel Id: undefined
Length: 43min 14sec (2594 seconds)
Published: Thu Jun 16 2011
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.