The post Five Reusable Code Snippets to Reduce Development Time appeared first on WebDevStudios.
]]>What you do, though, is iterate on that functionality and build upon the tools provided to you or the tools you have built for yourself in the past. After all, if you’re doing work around the house and need to hammer some nails into the wall, you’re not going to head into your garage to carve the wood and forge the steel to make a new hammer every single time (unless you’re our very own builder and wood magician Will Schmierer). You’ll use the same hammer to do the same job until the hammer is no longer useful or you find a new tool with which to replace it.
Why then would you not develop a site in the same way? Granted, most projects are going to require distinct sets of functionality unique to the projects themselves but there are plenty of ways you can reuse code to save time so you can truly focus on providing the best possible work with the best possible turnaround. Some of these reusable code snippets might not be necessary for every project, and that’s okay. Maybe you only need to use them a few times a year, but whenever the need arises you find yourself searching online or through your old code to find something you know you’ve written at least once before.
In this post, I’m going to share some things that have made their way into wd_s because we, as developers, find ourselves using similar functionality over and over again. Maybe you’ll find one, some, or all of these snippets useful. Whatever the outcome, I hope that I can help to trigger some thoughts on when and where you can reuse functionality in your own processes to more efficiently complete your projects.
Social networks, despite all the good and bad they put out into the world, are almost essential to have a digital life in this day and age. As such, there is rarely a website out there without links to the various social media accounts linked to the business or person attached to the site. In a WordPress install, there are several ways you could implement links to a set of social networks.
You could create a widget to manage the URLs, but what if you have many sidebars throughout your site and don’t want to deal with the tedium of manually replicating the same widget over and over again?
You could add URL fields to the Customizer and pull the data out into a template tag, but even then you’re limited by a finite number of fields in the Customizer. Perhaps, at launch, you only have Twitter and Facebook accounts, but six months down the road you spin up an Instagram account. Now, you’ve got to dive back into the code and go through the somewhat convoluted steps to add a new field to your Customizer settings.
You could even add the links to your social networks in a menu and then add a custom class to the menu items, like menu-icon-facebook
, and then target that class (or set of classes, for all of your networks) in your CSS. Still, that requires an extra step and if you’re building a site for someone who may not be familiar with WordPress’ menu interface this could be confusing or frustrating.
So, what’s the solution? This is what I’ve found to be useful time and time again. First, it can be helpful to register a brand new menu location for your social networks:
View the code on Gist.
By registering a menu, you’re making it easy to reuse that menu anywhere you want throughout your site. You can add it as a widget via the built-in Custom Menu widget; you can create a custom template tag in your theme to display the menu attached to that particular menu location, and any number of countless things that can be done with a WordPress navigation menu. You’re already making life easier for yourself by relying on WordPress to do a bunch of work for you!
You don’t want to have to worry about doing any extra work when adding your menu items. You want to create the menu, add your links, and have your social icons displayed through some sort of magic. Well, I don’t have any actual magic for you but I do have a Sass snippet that will get the job done for you.
By targeting the menu and the href
attribute values of the menu items, we can display the corresponding icon for each social network. Let’s take a look at that:
View the code on Gist.
First, you’re creating a $social-sites
Sass list which holds the names of all of our social networks. Be mindful of this and don’t try to get cute with the names as they have to correspond directly to the URL you’re providing for your links.
Next, you’re targeting the links within your menu and looping through your $social-sites
list to add an SVG as a background image. Your each
loop is going to target your link’s href
attribute and set the background image to match. This requires you to remember two things:
href
targeting will work as expected.icon-facebook.svg
), but just be sure to make sure your background-image
path matches your naming convention.Now, wherever you use that menu throughout your site you should “automatically” see the icon displayed. You’ll want to do some other work to make sure the text also doesn’t display so that you’re seeing just the icon, or tweak the styles as you see fit if you do wish to display both the icon and the link text. You have the power!
With social networks in mind, you’ll also want to make sure that any content shared from your site looks beautiful across every platform. There are plugins that will handle this for you, like Yoast SEO, but what if you don’t need a full SEO plugin or prefer to use a different solution for search engine optimization?
Fret not! OG and other essential meta tags can be a bit daunting at first, but with a recent update to wd_s
we’ve tried to take some of the guesswork out of them. There are a lot of conditions that you could, and should, check for if you’re writing your own tags. You may want to serve up different sets of data for different post types or taxonomies, in which case you would need to modify the example snippet a bit to suit your needs.
Of course, if you’re using a plugin like Yoast, which will also provide you with all of the fancy-schmancy meta tags you need to produce beautiful Twitter and Facebook cards, then we’ll want to avoid messing with them. At WDS, we don’t tend to run into many clients who want to use an SEO plugin other than Yoast, so the snippet you’ll see below includes a check for that specific plugin:
View the code on Gist.
If Yoast is active, bail so that we don’t wind up with duplicate meta tags causing headaches and collisions. If you make this a part of your framework, you’ll want to be sure to either add in the checks for any plugins you may use to manipulate OG tags or simply remember to remove the function if you know that you’re going to be using a plugin to handle these tags. This also future-proofs your theme, if you’re doing work for a client. Should they launch their site without Yoast and then decide to add it six months down the line, you’ve got them covered. Your custom meta tag solution won’t interfere with their activation of the plugin, thanks to the conditional at the top of the function.
You’re always going to have archive pages of some sort in WordPress, but for some reason, the titles of these pages are always a point of contention. Out of the box, WordPress creates a category archive page with a heading like “Category: Category Name.” Have you ever wanted to keep the prefix on that title? I certainly haven’t, and I don’t think we’ve had a single project in my (so far) seven years at WebDevStudios where a client wanted us to keep the prefix either.
If you’re working on a project for a client of your own, perhaps you never discussed this ahead of time. Since the archive pages are created dynamically, it may just be a passing note that the pages will retain the same look and feel as the rest of the site without much more discussion. However, more often than not, you’ll realize that everybody has an opinion on this after the fact even if they never stated their preference before.
Luckily, it’s pretty simple to remove the prefix from titles of these archive pages. So, instead of displaying “Category: Category Name” as the page title, you will instead display “Category Name.” Maybe you do want to add something before (or after) the base page title. Good news: you can! This snippet simply removes the prefix, but you can modify it to do any number of things to suit your needs from project to project. If nothing else, this is a handy snippet to keep on hand so you don’t have to Google “remove prefix from WordPress archive pages” every time you need to do this.
View the code on Gist.
This isn’t a huge one, as WordPress provides an out-of-the-box way to display pagination throughout a site via paginate_links
. So, what can we add to something already built into WordPress? We can make it easier to use and reuse in your templates. As you can see at the Codex link above, paginate_links
offers a whole slew of options of which you can take advantage. You can customize the previous and next link text and how many page numbers are displayed in your pagination amongst others.
The issue with just using the core template tag is that anytime you use it, you need to set your own values, if you want to override the defaults provided by WordPress. We can get around this by creating our own template tag and invoking paginate_links
with our own array of customizable options.
View the code on Gist.
In this snippet, we’re creating a template tag to power our paginated links. The function accepts an array, which means we can still customize the output of the links as if we were using paginate_links
by itself, but we have the bonus of being able to include our own set of default values so you never have to worry about filling those in whenever you want to use this function. Then, if you do happen to have a spot on your site where you want to override your own custom defaults, you simply need to call the new template tag with the array of custom values passed in. As our function is, more or less, a wrapper for paginate_links
, it accepts the same parameters for customization as seen in the Codex.
Nine times out of ten, the blog posts for your project are going to require some additional information displayed outside of title and content. We’re talking categories, tags, a posted-on date, and an author name. Once again, WordPress offers a number of ways to grab all of that information out of the box, but why toil around looking for the best way to display these extra details when you can just have it from the get-go? After all, am I going to need get_categories
, wp_list_categories
, get_the_category_list
, wp_get_post_categories
…? Stop the insanity!
Instead, let’s do this once and we can always tweak it on a per-project basis if need be, rather than starting with a blank canvas every single time. Let’s start at the top where we’ll want to display some important information: author and post date. With the following snippet, not only are we just getting the information we need, we’re also outputting it with some handy-dandy markup to make it even more usable.
View the code on Gist.
So now, you’ve digested the post title, author, and posted date. You’re ready to sit back and enjoy the contents of the post before you reach the end and look for some additional, related content and ready your nimble fingers to leave a friendly comment on the post (because you would never leave anything other than a friendly comment somewhere on the internet).
This snippet, just like the others in this post, can be modified to fit your needs. For us, we’re only going to try and display categories and tags on posts but you can adjust this for other post types or even rework the template tag on a deeper level to include custom taxonomies. First, we check to see if we’re in a post before checking for and displaying categories and tags. Then, if comments are open and the post isn’t protected, we’ll display the comments link. Finally, we’re displaying the edit post link so you can quickly jump back into edit mode if you need to do so.
View the code on Gist.
Now that you’ve got a handful of useful snippets to speed up your next project, what other ways can you think of to smartly reuse code from project to project smartly, and what are you going to do with all of that newfound spare time? Let us know in the comments below!
The post Five Reusable Code Snippets to Reduce Development Time appeared first on WebDevStudios.
]]>The post Get Loopy with Sass appeared first on WebDevStudios.
]]>As time passes the projects I tackle tend to present questions that require unique answers — answers that don’t always need a PHP or programmatic approach; found in the lesser known (or utilized) parts of Sass: control directives. Similar to PHP or Javascript, Sass has if()
, @if
, @else
, @else if
, @for
, @each
, and @while
that we can use to conditionally define or loop through styles using booleans or multiple variable checks. Why not save time and energy and make our compiler do the heavy lifting? You could even, in theory, automate the styles for an entire page or global elements. Let’s start off with the basics and end with a bang, then get into something more advanced.
if()
is a boolean expression that only returns one of two possible values: true or false, and it works like this.
if(true/false, true, false);
So a use case for this might be:
$boolean: false; .text-color { $color: if($boolean, #000, #fff); color: $color; } // .text-color
Which would output this:
.text-color { color: #fff; } // .text-color
That’s pretty simple. I’ve found this useful when building two containers with same styles, with the exception that their colors are inverted. Using if()
checks I can define either white or black depending on div-specific boolean variables.
@if
is different than if()
, though it is still a boolean, if()
is a singular check, while @if
evaluates a statement and runs code depending on the outcome. When used in conjunction with@else
you can create a loop to allow for multiple checks for true as well as a default should all prior nested statements prove false.
Let’s build on the example above:
$text: #fff; $boolean: false; .text-color { // Internal variables. $color: if($boolean, #000, #fff); // Determine background-color. @if $text == #000 { background-color: #fff; } @else { background-color: #000; } color: $color; } // .text-color
Notice here we’re using ==
, but just like Javascript or PHP you have the whole list of operators to choose from including arithmetic (+ - * / %
), equality (== !=
), logical (and or not
), and comparison (> >= < <=
) to determine whether or not a statement is true or false.
Bonus: If you want to up your game check out string and color operations.
Which would output this CSS:
.text-color { background-color: #000; color: #fff; } // .text-color
Though it does essentially the same thing, we allow for multiple conditionals and multiple possible outcomes with a few lines of code and one variable. A real time saver, and in my opinion, easier to read than a standard if()
clause.
We can also do multiple @if
checks with @else if
, for example:
@if $text == #000 { background-color: #fff; } @else if $text == #fc0 { background-color: #69ace5; } @else if $text == #273143 { background-color: #ccc; } @else { background-color: #000; }
Tip: Remember that numbers, letters, and variables are case sensitive when doing checks.
For those of you familiar with Javascript and jQuery, this might not be unfamiliar, but who knew?
@for
can be used two ways looping from x “through” y or from x “to” y. “through” starts and iterates to and including the last item while “to” iterates and does not include that one last iteration.
Here’s a simple @for
loop to help us generate helper classes for a grid framework:
@for $i from 1 through 4 { $width: 1 / $i; .col-#{$i} { width: $width; } // .col-#{$i} } // end @for
This would output to:
.col-1 { width: 100%; } .col-2 { width: 50%; } .col-3 { width: 33.33%; } .col-4 { width: 25%; }
Perhaps a more advanced usage might be to define the number of columns before you loop. You could also define mixins inside of the loop. Let’s take bourbon for instance.
$num-columns: 4; @for $i from 1 through $num-columns { .col-#{$i} { @include span-columns( $num-columns / $i ); } // .col-#{$i} } // end @for
Would output to this:
.col-1 { float: left; display: block; width: 100%; } .col-2 { float: left; display: block; width: 48.8217%; margin-right: 2.35765%; } .col-3 { float: left; display: block; margin-right: 2.35765%; width: 31.76157%; } .col-4 { float: left; display: block; margin-right: 2.35765%; width: 23.23176%; }
Note: If I was creating a grid, I might even add conditionals to make .col-1
output @include fill-parent;
or even add &:nth-child(x);
to remove the margin-right
from the last item in a column row.
each()
works like you’d expect: @each $variable in <list or map>
. You can loop through custom-defined class names to define a color or image (example below), which makes styling similar items a snap.
$type: action, western; @each $movie in $type { .#{$movie}-container { background-image: url("assets/images/#{$movie}-bg.jpg"); } // .#{$movie}-container } // end @each
Output:
.action-container { background-image: url("assets/images/action-bg.jpg"); } // .action-container .western-container { background-image: url("assets/images/western-bg.jpg"); } // .western-container
I would say, super helpful. You can go one step further and define multiple variables within a map
.
$type: (action, #fc0), (western, #8a472c); @each $movie, $color in $type { .#{$movie}-container { background-image: url("assets/images/#{$movie}-bg.jpg"); color: $color; } // .#{$movie}-container } // end @each
Which becomes:
.action-container { background-image: url("assets/images/action-bg.jpg"); color: #fc0; } // .action-container .western-container { background-image: url("assets/images/western-bg.jpg"); color: #8a472c; } // .western-container
Using Sass maps is super helpful within loops. Check those out here.
I rarely use @while
loops. You can accomplish most everything you might need to achieve with @if
or @each
directives. That said, they are sometimes useful. Particularly within a @mixin
since they run if a condition is met avoiding extra code.
$num: 4; @while $num > 0 { .block-#{$num} { content: "#{$num}"; } // .block-#{$num} // Reset loop and go again. $num: $num - 1; } // end @while
This outputs:
.block-4 { content: "4"; } // .block-4 .block-3 { content: "3"; } // .block-3 .block-2 { content: "2"; } // .block-2 .block-1 { content: "1"; } // .block-1
Separately, any of these can be a major help, but you generate more markup to turn a @mixin
into something like a function for Sass.
Here’s an example with documentation, which outputs a standard hamburger button.
//----------------------------------------- // Make the Hamburger Icon //----------------------------------------- @mixin generate_hamburger($width, $height, $space, $color, $position, $radius, $speed) { // Start variables. $gutter: $height + $space; // Determine position left right or center. @if $position == right { float: right; } @else if $position == left { float: left; } @else { margin-left: auto; margin-right: auto; } margin-bottom: $height + $space; margin-top: $height + $space; position: relative; text-indent: -9999em; // All bar sizes. &, &::before, &::after { background-color: $color; // Border radius? @if $radius != 0 { border-radius: $radius; } height: $height; transition: all $speed ease-in-out; width: $width; } // &, &::before, &::after // Top/bottom bars. &::before, &::after { content: ""; left: 0; position: absolute; } // &::before, &::after // Top bar. &::before { top: -$gutter; } // &::before // Bottom bar. &::after { bottom: -$gutter; } // &::after } //----------------------------------------- // Usage //----------------------------------------- .menu-toggle { @include generate_hamburger(30px, 5px, 5px, #000, null, 5px, 0.3s); } // <div class="menu-toggle"></div>
You could take this even further to add a focus state with some animation doing like this. We’ll add a is-active
class and some js to help us along.
//----------------------------------------- // Make the Hamburger Icon //----------------------------------------- @mixin generate_hamburger($width, $height, $space, $color, $position, $radius, $speed) { // Start variables. $gutter: $height + $space; // Determine position left right or center. @if $position == right { float: right; } @else if $position == left { float: left; } @else { margin-left: auto; margin-right: auto; } margin-bottom: $height + $space; margin-top: $height + $space; position: relative; text-indent: -9999em; // All bar sizes. &, &::before, &::after { background-color: $color; // Border radius? @if $radius != 0 { border-radius: $radius; } height: $height; transition: all $speed ease-in-out; width: $width; } // &, &::before, &::after // Top/bottom bars. &::before, &::after { content: ""; left: 0; position: absolute; } // &::before, &::after // Top bar. &::before { top: -$gutter; } // &::before // Bottom bar. &::after { bottom: -$gutter; } // &::after // Active state. &.is-active{ @include activate_hamburger(#675aac); } // &.is-active } // If active. @mixin activate_hamburger($color) { // Top/bottom bars. &::before, &::after { background-color: $color; } // &::before, &::after // Top bar. &::before { top: -1px; transform: rotate(45deg); } // &::before // Bottom bar. &::after { bottom: 1px; transform: rotate(-45deg); } // &::after } //----------------------------------------- // Usage //----------------------------------------- .menu-toggle { @include generate_hamburger(30px, 5px, 5px, #000, null, 5px, 0.3s); } // <div class="menu-toggle"></div>
See the Pen Hamburger Mixins by jomurgel (@jomurgel) on CodePen.
Sometimes it’s the little things. With the goal to optimize load times, increase proficiency, and generate dynamic content easily, it’s a no-brainer that utilizing control directives in Sass is the way to go. At the very least, it can cut your dev time in half on certain tasks. I wholeheartedly recommend taking a deep dive into Sass and the power of if()
, @if
, @else
, @else if
, @for
, @each
, and @while
.
We’d love to know how people use loops in new and exciting ways. Let us know in the comments below.
The post Get Loopy with Sass appeared first on WebDevStudios.
]]>The post Is Clever Code OK? appeared first on WebDevStudios.
]]>
For most, clever code is code that does not immediately “explain” itself. What that means is that the average developer might see it and have to spend more than a few seconds reading the context around it to understand what it does. Here’s an example in PHP:
$foo = 'bar'; // snip $value = $foo ?: false;
Considering I’ve been writing PHP for nearly sixteen years, I was bewildered when I first saw this in a co-worker’s code a few weeks ago.
“Pretty nifty, huh?”
You bet! I want to use that in the future, and it is a prime example of clever code–the slang of a programming language.
If you’ve used clever code in the workplace, or in other projects where collaboration is a part of what you do, then you’ve probably run across a decent number of people who don’t like it. Some examples of feedback:
All very reasonable responses. Clever code is often cited as bringing a disadvantage beyond reading comprehension; it’s also viewed as a source of bugs. Implementing code that is harder to read means it’s more likely that you or a developer working in the same file later are more likely to not read the proper flow of the program. Here’s an example of using PHP’s Bitwise And operator:
// Bitwise And. $foo = false; $foo &= false; $foo &= true; $foo &= false; var_dump( $foo ); // int(0) $foo = false; $foo &= false; $foo &= true; var_dump( $foo ); // int(0)
In the first example, you might expect the outcome of false, or “int(0)”. But the second example is a bit confusing–didn’t that end with setting $foo to true? The Bitwise And operator instead looks at both the left-hand value of “$foo” and the right-hand value when making its assignment; thus, if $foo is false even once, the equation will always look like “$foo = ( 0 * 1 = 0 = false ).” This can be extremely useful, but also incredibly confusing.
Another example of Bitwise confusion comes from the Javascript Bitwise NOT operator:
var a = ["f", "o", "o", "b", "a", "r" ]; if ( a.indexOf( "g" ) > -1 ) { console.log( "Typical style" ); } if ( !!~a.indexOf( "g" ) ) { console.log( "Clever style" ); }
Again, we can see that the “tilde” operator, coupled with a double negative “!!”, creates clean code that won’t make sense to the average programmer.
This all leads to what I’d like programmers to start doing when considering clever code: create a learning environment for other developers. I’ve seen clever solutions criticized on StackOverflow, and a particular question about it on the Programmer’s StackExchange asking for advice on how to avoid writing clever code in the first place.
This, to me, is a great example of how programmers see clever code: it’s a party trick; it’s dangerous; it is to be avoided by “serious” programmers.
On the other hand, I think we can start to turn clever code into a learning experience. Once you recognize clever code, you can start to document it when you use it so others on the project know what you’re doing:
function check_user( $can_go_on ) { /** * NB, Clever: Using PHP's bitwise And operator. * See: http://php.net/manual/en/language.operators.bitwise.php */ if ( ! current_user_can( 'manage_network' ) ) { $can_go_on &= false; } return $can_go_on; } function check_screen( $can_go_on ) { $screen = get_current_screen(); if ( ! $screen || 'post' !== $screen->base ) { $can_go_on &= false; } return $can_go_on; } add_filter( 'can_we_continue', 'check_user' ); add_filter( 'can_we_continue', 'check_screen' ); $can_go_on = apply_filters( "can_we_continue", true );
In the above example, the ‘check_user’ function is documented to give a clear and distinct “Hey! I’m doing something weird here,” as opposed to the second method ‘check_screen’ which quietly uses the Bitwise And without any discussion. Most any programmer would be forgiven for missing it or thinking it’s a typo, and a scrutinizing lead would almost certainly see it and think “Great–now I have to change this and test everything this filter touches.”
A real world example looks like this:
/** * CLEVER CODE ALERT. This shorthand ternary accepts the tested value as the value returned if * true, otherwise the else value. NB this does not work in reverse (i.e. $a = $b ? 'value' :; ). */ $blog_id = $blog_id ?: get_current_blog_id();
Here’s that nifty shorthand ternary again! This saves some characters and looks pretty neat, but there’s a pretty hefty comment above it to explain what’s going on. The comment alone negates the time saved in not writing out the whole ternary, but for me the point of code like this, and the associated documentation, is to teach and inform others.
As programmers working in an increasingly collaborative world, we should be making strides in documentation–not only to guide other developers through the big ins and outs, but also to illuminate the many tricks and secrets that the language holds.
Happy coding!
The post Is Clever Code OK? appeared first on WebDevStudios.
]]>The post Using Sass Mixins to Power Your Projects appeared first on WebDevStudios.
]]>Please note: This post will assume that you have at least a basic understanding of Sass and have used it at least a handful of times.
Today, we’ll be skipping over some of the basics like variables, nesting, and partials and get to some of the more intermediate working of Sass, specifically mixins, and we’ll touch on placeholders briefly as well. Mixins are a fantastic way to create and package blocks of reusable code so that you don’t have to continuously write out items that can become repetitive and sometime tedious (not to say they’re not important).
Before we completely dive into some cool mixins I’ve come across and libraries to checkout, I will quickly touch on placeholders vs. mixins. There’s been quite a bit of discussion around the web on the topic, but it’s a bit all over the place, so I wanted to cover some of what I found while doing some research for this post.
I think this first post by Hugo Giraudel speaks to front-end development work in general: “It depends.”
Hugo goes on to say:
The best advice would be: if you need variables, use a mixin. Otherwise, extend a placeholder. (placeholder vs mixin).
I think that’s solid advice and typically how I approach the topic as well. What’s also worth noting here is that Shay Howe did some pretty extensive research on the topic, the results of which I found as surprising as he did in his article, “Sass Mixins vs Extends the Data.”
It seems as though big picture, mixins create a bit more bloat in the compiled output and that they resulted in faster load time overall, which was interesting and seemed noteworthy as that’s an important detail not to be overlooked.
Despite that discovery, I still think both have their place and can be useful in certain projects. More or less, that puts us back to where we started–it depends on the project and situation, as always! With all front-end development, as long as it works, makes sense, and has been carefully thought through, there’s no reason not to use both in a given project from my perspective.
Here at WDS, we have several useful mixins included in our wd_s theme that are worth checking out. They’re particularly useful for our most common use cases. In this post, I will highlight some additional useful (less common) mixins that I’ve come across and used before. We haven’t necessarily included them in wd_s (at least not yet), because we intentionally keep things relatively lean there.
If you’re new to mixins, it would be wise to make sure you have a basic understanding. The following list is by no means comprehensive, but I tried to find a few that we’ve either used here at WDS or didn’t appear on every other list of “Top 10 Mixins that Should Be in Your Toolbox Post.” That’s not to say they won’t appear in other places–they are just not quite as common as breakpoint mixins or px to rem conversions.
We all know cross browser compatibility is certainly improving with modern browsers, but even today, the struggle is real, and there are lots of projects that still require backwards compatibility for various reasons. This one should work all the way back to and including IE6.
@mixin opacity($opacity) { $opacity-ie: $opacity * 100; filter: alpha(opacity=$opacity-ie); //IE8 opacity: $opacity; } //Usage .faded-text { @include opacity(0.8); }
Centering elements vertically, horizontally, or in some cases both axis, can always be a bit of a struggle for a variety of reasons. This mixin requires the parent element to have a position of relative to work and the mixin should be applied to the child element you’re trying to center vertically/horizontally or both. Here is the mixin–and be sure to check it out in its full glory on CSS-tricks.com.
@mixin center($horizontal: true, $vertical: true) { position: absolute; @if ($horizontal and $vertical) { top: 50%; left: 50%; transform: translate(-50%, -50%); } @else if ($horizontal) { left: 50%; transform: translate(-50%, 0); } @else if ($vertical) { top: 50%; transform: translate(0, -50%); } }
This is particularly useful for combining fonts and font weights. I find this one super useful for when you have to manage multiple fonts and weights throughout a project. It’s also super useful also if you are working in a multilingual project and certain languages require different fonts within the same project. Hat tip to Stacy Kvernmo, the original creator of this.
This may not be an everyday use case mixin, but it was used on a recent project and came in pretty handy considering the number of times we had to implement a partial title border that didn’t need to be flexible depending on which section of the site or particular page a user might be visiting. This particular version has a number of arguments you can pass through which was a nice touch and well done. Hat tip to fellow teammate Allison Tarr on this one.
// Title Border // Apply a short border beneath a title // Usage @mixin title-border(color); @mixin title-border($color, $height, $width, $position) { @if $position == center { $position: 0 auto; } @else { $position: 0; } &:after { border-bottom: $height solid $color; content: ''; display: block; margin: $position; padding-bottom: rem(15); width: $width; } } //Use Case: .h1 { @include title-border($color, rem(5), rem(85), left); }
While poking around on CodePen, I came across this sticky footer mixin by Zoe Rooney. I quite like this and could see some practical use cases for this being used as we begin (hopefully) the trend away from hamburger menu for navigation. As mobile phone use continues to grow each month for everyday web browsing, this seems like a very practical application.
Although I will say I’m not a super huge fan of tooltips, there are some fairly good use cases for them from time to time throughout a project so long as they’re not abused. This tooltip mixin uses only HTML and CSS(SCSS), and is a nice combination of a few mixins to make this a well done reality.
These are just a few mixins and use cases for them. In addition to these, and the ones in our wd_s starter theme, there are a number of libraries worth looking into as well for mixins and may already be incorporated into your workflow, like Bourbon.io (part of wd_s).
Some noteworthy libraries to also check out. Full disclosure: I’ve not thoroughly gone through all of these but they look worthy of investigating further:
Sass mixins are undoubtedly powerful and useful for building all types of sites. Do you have any mixins that you particularly like or use often that you’ve found helpful for your own projects? If so, feel free to share them in the comments! We’d love to hear from you!
The post Using Sass Mixins to Power Your Projects appeared first on WebDevStudios.
]]>The post DIY: Twitter Share Counts (Part 1) appeared first on WebDevStudios.
]]>
To get going, you’re going to need two things at minimum. First and foremost, I’m not going to go into detail about how OAuth works, so for that we’ll use Abraham’s Oauth Library. If you don’t use composer, click the Manual Installation tab, and download it from his GitHub. Secondly, you’ll need a Twitter app. For this DIY, we’re going to be bending the REST API to do our bidding, and for that, you need an app. If you want to wing it and think you’ll be okay without instructions, here’s a handy link to get you there. If you’re not entirely sure how to register an app with Twitter, follow this blog post on iag.me which shows you how to register a Twitter app.
Once you make your app, go to ‘Keys and Access Tokens’ and note the following (you’ll need them in the code coming up):
For the purposes of this tutorial, we’re going to use a simple singleton class. We know that for a definite we need a template tag to display the count. One other thing to keep in mind is Twitter’s rate limit; each API call has its own limits, so for this we’re going to use the GET search/tweets endpoint, which has a rate limit of 180 calls per fifteen minutes. Due to this rate limit, you want to make sure to cache the resulting count; for this I’m using transients, however, if you have a persistent cache like WP Engine, you may want to use wp_cache_get/set functions instead. So here’s our scaffolding:
<?php class Twitter_Counts { /** * @var Twitter_Counts null */ public static $instance = null; private function __construct() { // Fancy stuff. } public static function get_instance() { if ( is_null( self::$instance ) ) { self::$instance = new self; } return self::$instance; } public function tweet_count( $post_id ) { } } function Twitter_Counts() { return Twitter_Counts::get_instance(); } function display_tweet_counts( $post_id = 0 ) { if ( empty( $post_id ) ) { $post_id = get_the_ID(); } $cache_key = md5( 'twitter_counts_' . $post_id ); $count = get_transient( $cache_key ); if ( false == $count ) { $tc = Twitter_Counts(); // ... do stuff } return $count; }
Now that the scaffolding is setup, we need to start talking to Twitter with the OAuth library you downloaded. So setting it up is insanely easy (which is why I love this library):
require_once 'twitteroauth/autoload.php'; use Abraham\TwitterOAuth\TwitterOAuth; class Twitter_Counts { /** * @var Twitter_Counts null */ public static $instance = null; private $consumer_key = ''; private $consumer_secret = ''; private $access_token = ''; private $access_secret = ''; private function __construct() { // Fancy stuff. } public static function get_instance() { if ( is_null( self::$instance ) ) { self::$instance = new self; } return self::$instance; } public function tweet_count( $post_id ) { $oauth = new TwitterOAuth( $this->consumer_key, $this->consumer_secret, $this->access_token, $this->access_secret ); } }
If you are using Composer, you can ignore the first two lines. For me, I downloaded the library into a twitteroauth folder. Below that, you’ll see that there are new private variables. Since these are basically like passwords, it’s best if they’re inaccessible to anyone but the main class (although of course your requirements may be different and you’ll have to accommodate for that accordingly). Here is where those app values you copied from Twitter will come in handy; you’ll need to fill in these variables.
Line 29 is where the values are used. This literally does the OAuth handshake for you, and now all we have to do is make the request we want and process the results.
Using the OAuth library makes it simple to do get requests. If you want to know all the parameters for the endpoint we’re using, you’ll need to consult the official search/tweets endpoint documentation. For now, we only need to worry about q, count, and include_entities.
Since we’re using the search endpoint, we need to search something unique to the page we’re looking at, or wanting counts for, that would be included in the tweet. Can’t get much more unique than the URL, right? We also want to return as many results as possible, this will help us in possibly going around the rate limit (unless you have a page with a million likes). For this, we set count to 100. Finally, we want to make sure to include Entities, since from what I can tell, those include the original URL prior to it being converted to the t.co shortener.
The code should look something like this:
public function tweet_count( $post_id ) { $defaults = array( 'q' => get_permalink( $post_id ), 'count' => 100, 'include_entities' => true, ); $oauth = new TwitterOAuth( $this->consumer_key, $this->consumer_secret, $this->access_token, $this->access_secret ); $statuses = $oauth->get( 'search/tweets', $defaults ); }
Looking at the results on the official documentation you’ll see that you get back a JSON object. A quite large one in fact, but don’t let that scare you, in the end, it’s all data, and we tell it what to do! So what do we do? Well, since the JSON data is keyed, you’ll see the main key we’re concerned with, statuses. Lastly we should also check if the property is available after the transformation by using an isset check.
Having as many checks as necessary prevents your debug log filling up. Alternatively, if you want to log these errors, you can do so in a much nicer manner. For that, you should read my other post on Debugging WordPress Tips and Snippets.
Now that we got those checks out of the way, it’s a simple as running count() over the statuses. The code goes like so:
public function tweet_count( $post_id ) { $defaults = array( 'q' => get_permalink( $post_id ), 'count' => 100, 'include_entities' => true, ); $oauth = new TwitterOAuth( $this->consumer_key, $this->consumer_secret, $this->access_token, $this->access_secret ); $statuses = $oauth->get( 'search/tweets', $defaults ); if ( ! $statuses ) { return false; } if ( ! isset( $statuses->statuses ) ) { error_log( __LINE__ ); return false; } return count( $statuses->statuses ); }
Now we have to wrap up–this is the simple part! Here we need to update our display_tweet_counts() template tag to actually use our tweet counting method. Since our count method can return a boolean value (true/false) we want to check for that and set the count to zero if there was a problem. Otherwise, we want to use the actual value.
So here’s the full code:
require_once 'twitteroauth/autoload.php'; use Abraham\TwitterOAuth\TwitterOAuth; class Twitter_Counts { /** * @var Twitter_Counts null */ public static $instance = null; // You'll need to fill these in with your own data. private $consumer_key = ''; private $consumer_secret = ''; private $access_token = ''; private $access_secret = ''; private function __construct() { // Fancy stuff. } public static function get_instance() { if ( is_null( self::$instance ) ) { self::$instance = new self; } return self::$instance; } public function tweet_count( $post_id ) { $defaults = array( 'q' => get_permalink( $post_id ), 'count' => 100, 'include_entities' => true, ); $oauth = new TwitterOAuth( $this->consumer_key, $this->consumer_secret, $this->access_token, $this->access_secret ); $statuses = $oauth->get( 'search/tweets', $defaults ); if ( ! $statuses ) { return false; } if ( ! isset( $statuses->statuses ) ) { return false; } return count( $statuses->statuses ); } } function Twitter_Counts() { return Twitter_Counts::get_instance(); } function display_tweet_counts( $post_id = 0 ) { if ( empty( $post_id ) ) { $post_id = get_the_ID(); } $cache_key = md5( 'twitter_counts_' . $post_id ); $count = get_transient( $cache_key ); if ( false == $count ) { $tc = Twitter_Counts(); $result = $tc->tweet_count( $post_id ); $count = false == $result ? 0 : $result; set_transient( $cache_key, $count, 1 * HOUR_IN_SECONDS ); } return $count; }
That comes in part two, so stay tuned! In part two, we’re going to get into recursion, and how to walk over the results page-by-page. Keep an eye out for the second installment, and let me know if you have any questions!
The post DIY: Twitter Share Counts (Part 1) appeared first on WebDevStudios.
]]>The post The Future of JavaScript: ECMAScript 6 and You appeared first on WebDevStudios.
]]>When I started writing basic web pages in the late 90’s, “DHTML” was the acronym describing the use of JavaScript, HTML, and CSS; the “D” stood for “Dynamic.” DHTML sites were heavy and sluggish, yet they showcased the power of JavaScript (or JScript, depending on who you asked). They also served as a reminder of why many developers shied away from JavaScript at the time: it was resource-intensive, and implementations varied from browser to browser. At the time, I only knew a handful of people who did any kind of web development–we were, after all, more concerned with the issues of how much AP courses “sucked” and whether or not we could get tickets to see Blink 182. For us, the consensus was that JavaScript was for show, and any website could do what it needed to do without it.
JavaScript has now made a name for itself as the go-to for interactive sites. Gone are the days of full-page Flash applications, Shockwave Player, and Java as a general “necessity” for the web. JavaScript has even found it’s way onto the server with projects such as Node.js. Automation of screenshots, converting web pages to PDF, or headless testing can all be achieved from the command link using PhantomJS, a headless implementation of the WebKit engine. On the wider web, JavaScript gives us the little spinner that eventually leads to a table of search results populating without a page reload, or dragging-and-dropping an image to upload it to Imgur. Even some of the apps on the smartphones in pockets around the world use JavaScript. It’s no longer just for hobbyists or experimentation – knowing JavaScript is a part of the job.
Which brings us to ECMAScript 6, or ES6. JavaScript is an implementation of ECMAScript, which also covers languages such as ActionScript, JScript, QtScript, and many others. ES6, codenamed “Harmony,” has been in the works since 2008 and periodic drafts have been published since 2011. This month, June 2015, ECMAScript 6 was formally announced and released and includes a lot of new and interesting tidbits for developers of the modern web.
Below I hope to cover some of the new features, but by no means all of them, as that would be far beyond the scope of this post. For more information, you can check out the full specification PDF to get a look at all of the stuff coming to a browser near you.
Author’s note: the following will include some code examples – some modern browsers may still not recognize certain keywords or agree with their use. For instance, Chrome requires “use strict” to make use of the “let” keyword.
A simple idea in many languages, Constants have been absent in JavaScript until now. In programming, a constant is similar to a variable, in that you assign a value to an identifier, but differs in that it can never be changed throughout execution of the program. An example in PHP:
define('APPLICATION_VERSION', '1.2.0.4332'); echo APPLICATION_VERSION; // 1.2.0.4332 define('APPLICATION_VERSION', '1.3'); // Notice: Constant APPLICATION_VERSION already defined echo APPLICATION_VERSION; // 1.2.0.4332
The constant APPLICATION_VERSION is defined and can never be altered, and PHP will let you know that if you try (and your error reporting is on). Attempts to redefine the constant will fail, and the original value will hold true. In Javascript, this was previously achieved by the clunky mechanism of defining a new Object property–this was only available in ES5, and only in the global scope. With ES6, things get a lot easier:
const APPLICATION_VERSION = '1.2.0.4332'; console.log(APPLICATION_VERSION); // '1.2.0.4332' APPLICATION_VERSION = '1.3.0'; // "1.3.0" console.log(APPLICATION_VERSION); // '1.2.0.4332' // Notice that the Chrome console evaluated the assignment // on line 3, however the value didn't change
If we try to actually redefine the constant we get an error:
While a simple addition, the introduction of constants to JavaScript shows that the language is maturing in a way that will be welcome by many programmers.
How about something a little more interesting?
If you have programmed in JavaScript, you’ve dealt with all of the ways in which default parameters can be mimicked in the language:
// The "if" way function say_hi(iterations, greeting, name) { if( iterations === undefined ) { iterations = 5; } if( greeting === undefined ) { greeting = 'Hello'; } if( name === undefined ) { name = 'World'; } for ( var i = 0; i < iterations; i++ ) { console.log( greeting + ', ' + name ); } } // The "||" way function say_hi(iterations, greeting, name) { iterations = iterations || 5; greeting = greeting || 'Hello'; name = name || 'World'; for ( var i = 0; i < iterations; i++ ) { console.log( greeting + ', ' + name ); } } // The jQuery "extend" way function say_hi(args) { var params = {}; var defaults = { iterations: 5, greeting: 'Hello', name: 'World' }; jQuery.extend(params, defaults, args); for ( var i = 0; i < params.iterations; i++ ) { console.log( params.greeting + ', ' + params.name ); } }
Above we have several methods to handle “default parameters,” none of which are really ideal. The first method is clunky, the second method is cleaner but not necessarily self-explanatory (especially to novice programmers), and the third method uses jQuery’s “extend” method–this is a great way to define a default set of options in an arguments object, but this also obfuscates the intent of the function – you wouldn’t know it took those arguments just by looking at the method signature, and seeing it in use without one of the object properties defined would leave you oblivious to the fact that you could pass that property without reading the documentation or diving into the source.
ES6 introduces default variables in a way that should be familiar to most programmers. The new method is clean and readable:
function say_hi( iterations = 5, greeting = 'Hello', name = 'World' ) { for ( var i = 0; i < iterations; i++ ) { console.log( greeting + ', ' + name ); } }
At the time of writing, this only works in Firefox (versions 15 and on support basic defaults, while versions 26 and 41 introduce more complex usage). Chrome has an open support ticket to implement this feature in the V8 JavaScript engine, and any browser working towards ES6 should be following suit.
Something I’ve done for a long time, templating with strings, usually requires custom functionality, regular expressions, and somewhat clunky code. The separation of template and data can hide how things tie together. Here’s an example done with an Ext.JS-like templating function:
function tpl( data, template ) { var r = new RegExp(); for ( var key in data ) { if ( ! data.hasOwnProperty( key ) ) { continue; } r.compile( '{' + key + '}', 'g' ); template = template.replace( r, data[ key ] ); } return template; } var myTplData = { name: 'John Smith', address: '4124 Rasperry Lane', city: 'Heartford', state: 'CT' }; var myTpl = [ '<div>', '<strong>Name: </strong> {name} <br/>', '<strong>Street Address: </strong> {address} <br/>', '<strong>City, State: </strong> {city}, {state} <br/>', '</div>' ].join("\n"); console.log( tpl( myTplData, myTpl ) ); /** <div> <strong>Name: </strong> John Smith <br/> <strong>Street Address: </strong> 4124 Rasperry Lane <br/> <strong>City, State: </strong> Heartford, CT <br/> </div> */
The above code isn’t exactly great, and rather simple. When you look at Ext.JS’s XTempalte, you see just how much complexity gets added to what sounds like a straightforward concept. Other libraries provide their own methods of binding data to templates, but ES6 can at least begin to replace some of this functionality with an easier approach:
var myTplData = { name: 'John Smith', address: '4124 Rasperry Lane', city: 'Heartford', state: 'CT' }; var myTpl = ` <div> <strong>Name: </strong> ${myTplData.name} <br/>, <strong>Street Address: </strong> ${myTplData.address} <br/>, <strong>City, State: </strong> ${myTplData.city}, ${myTplData.state} <br/> </div>`; console.log( myTpl ); /** <div> <strong>Name: </strong> John Smith <br/>, <strong>Street Address: </strong> 4124 Rasperry Lane <br/>, <strong>City, State: </strong> Heartford, CT <br/> </div> */
This completely removes the need for Regular Expressions, and carries a syntax somewhat familiar to those who use PHP and other languages. Integration of variables and strings has been long overdue for JavaScript, so it’s good to see ES6 finally make a change.
These are just a few of the myriad features coming in ECMAScript 6. While some of these exist in current browser implementations (notably Chrome and Firefox), we likely won’t see the full specification implemented for a few months, and adoption across the board will likely take much longer. That doesn’t mean there aren’t more things to look into and get excited about, such as Maps, Classes, Iterators and Generators, and more.
The post The Future of JavaScript: ECMAScript 6 and You appeared first on WebDevStudios.
]]>The post Create Simple Inline Media Queries with include-media appeared first on WebDevStudios.
]]>Eduardo Bouças, developer of include-media, states on the website:
“include-media is a Sass library for writing CSS media queries in an easy and maintainable way, using a natural and simplistic syntax.”
While there are many libraries out there to choose from, I really enjoy the syntax used by include-media. When writing code, it is always helpful to be able to translate what you are writing to your native language (in your head, of course).
I love using the operators as it is so simple to see what is taking place. Many media query libraries do not take into account the max value of the smaller breakpoint and the minimum value of the larger one, which means both media queries will be active at that same time potentially causing conflict. Having the ability to use the >=
(greater than or equal to) or <=
(less than or equal to) is very exciting.
Bring your own Breakpoint names and values! By default, include-media will help you get started with the following:
$breakpoints: ( phone: 320px, tablet: 768px, desktop: 1024px );
I recommend that you redeclare these breakpoints based on when your content actually needs a breakpoint. You can also use your own naming conventions as well if device name is not your thing. To redefine your breakpoints, use the same Sass Map syntax. include-media can accept px, em or rem units:
$breakpoints: ( phone: 24em, tablet: 50em, desktop: 72em );
Now let’s put these babies to use:
.primary { // look at me, I’m mobile first and full width width: 100%; // then when I reach a point where my main content area may be too wide // we can now call in our breakpoint @include media(">phone", "<=tablet") { width: 35%; } }
How simple is that? I love speakable code.
Do you need an on-the-go breakpoint for those one-off situations? I am in favor of making variables for as many values as possible, but if that is your style this is for you:
@include media(">desktop", "<=2600px") { content: 'Why is your browser so wide?'; }
You can target media types screen, print, handheld the same way you’d target a size:
.primary { content: 'screens are awesome'; @include media("print") { content: 'people print websites?'; } }
Or target retina screens:
.primary { // I’m a regular sized logo background-image: url(images/logo.png); // I’m a SUPER logo for pretty retina screens @include media("retina2x") { background-image: url(images/logo@2x.png); } }
To get started, you can either download the single scss file and @import it to your project manually or you can install via Bower:
bower install include-media
and import dist/_include-media.scss
into your project.
The post Create Simple Inline Media Queries with include-media appeared first on WebDevStudios.
]]>The post Use CMB2 to Create a New Post Submission Form appeared first on WebDevStudios.
]]>We do this for several reasons:
One of the cool and powerful ways we have used CMB2 is for creating front-end submission forms for our users. Traditionally we (and I’m sure many of you) have used Gravity Forms as your go-to front-end form solution. No doubt Gravity Forms is an incredible tool, but we decided for some use-cases, it isn’t the right tool.
For example, if I want to provide a front-end blog post (or other post-type) submission form, I want to avoid Gravity Forms. With a submission form like that, you generally want full control of how it looks, when/where it should display and where the data goes when submitted. You probably don’t want that form created in wp-admin where the site’s owner or users can edit it. Enter CMB2.
I’m going to walk you through how to create a front-end submission form with CMB2.
First step, we need to register our form. This is standard form and field registration:
/** * Register the form and fields for our front-end submission form */ function wds_frontend_form_register() { $cmb = new_cmb2_box( array( 'id' => 'front-end-post-form', 'object_types' => array( 'post' ), 'hookup' => false, 'save_fields' => false, ) ); $cmb->add_field( array( 'name' => __( 'New Post Title', 'wds-post-submit' ), 'id' => 'submitted_post_title', 'type' => 'text', 'default' => __( 'New Post', 'wds-post-submit' ), ) ); $cmb->add_field( array( 'name' => __( 'New Post Content', 'wds-post-submit' ), 'id' => 'submitted_post_content', 'type' => 'wysiwyg', 'options' => array( 'textarea_rows' => 12, 'media_buttons' => false, ), ) ); $cmb->add_field( array( 'name' => __( 'Featured Image for New Post', 'wds-post-submit' ), 'id' => 'submitted_post_thumbnail', 'type' => 'text', 'attributes' => array( 'type' => 'file', // Let's use a standard file upload field ), ) ); $cmb->add_field( array( 'name' => __( 'Your Name', 'wds-post-submit' ), 'desc' => __( 'Please enter your name for author credit on the new post.', 'wds-post-submit' ), 'id' => 'submitted_author_name', 'type' => 'text', ) ); $cmb->add_field( array( 'name' => __( 'Your Email', 'wds-post-submit' ), 'desc' => __( 'Please enter your email so we can contact you if we use your post.', 'wds-post-submit' ), 'id' => 'submitted_author_email', 'type' => 'text_email', ) ); } add_action( 'cmb2_init', 'wds_frontend_form_register' );
As you can see, we’ve used the new CMB2 form and field registration API to register our front-end form and fields. We also used a new parameter from version 2.0.3: 'save_fields' => false
. This prevents CMB2 from saving the fields, so we can do what we want with the submission.
We added fields for the post title, post content, and optional fields for the featured image, contributor name, and contributor email. The first item in the 'object_types'
array will be used for the new post submission 'post_type'
.
Now that we have our form registered, let’s go ahead and register a shortcode handler for our front-end submission form. This adds flexibility as the site owner can place this shortcode anywhere they want. We’ll also allow a few shortcode attributes that we’ll cover later.
/** * Handle the cmb-frontend-form shortcode * * @param array $atts Array of shortcode attributes * @return string Form html */ function wds_do_frontend_form_submission_shortcode( $atts = array() ) { // Current user $user_id = get_current_user_id(); // Use ID of metabox in wds_frontend_form_register $metabox_id = 'front-end-post-form'; // since post ID will not exist yet, just need to pass it something $object_id = 'fake-oject-id'; // Get CMB2 metabox object $cmb = cmb2_get_metabox( $metabox_id, $object_id ); // Get $cmb object_types $post_types = $cmb->prop( 'object_types' ); // Parse attributes. These shortcode attributes can be optionally overridden. $atts = shortcode_atts( array( 'post_author' => $user_id ? $user_id : 1, // Current user, or admin 'post_status' => 'pending', 'post_type' => reset( $post_types ), // Only use first object_type in array ), $atts, 'cmb-frontend-form' ); // Initiate our output variable $output = ''; // Our CMB2 form stuff goes here return $output; } add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );
So we’ve registered our shortcode, cmb-frontend-form
and supplied a callback, wds_do_frontend_form_submission_shortcode
. Because our shortcode can accept a few attributes, we’re passing in the $atts
array. This will be the array of attributes already parsed out for us by WordPress (if the shortcode has any attributes).
Next, you follow the inline comments to see what is happening.
We are:
$metabox_id
variable. It is the same value as the 'id'
parameter we used when registering our metabox/form.$object_id
variable. This looks hokey, but it allows us to prevent CMB2 from auto-magically finding an object id (which could end up being the post ID of the page the form lives on.. not what we want!). This will ensure that the form remains empty.$cmb
metabox instance by passing the metabox/form id we registered in our first snippet, 'front-end-post-form'
.'object_types'
property from the $cmb
object. This object type will be the default 'post_type'
for the new post submission (unless overridden by the shortcode attributes).$output
variable that we’ll be appending (concatenating) too.To parse the shortcode attributes, we use the shortcode_atts
function. This function, according to the Codex, “…Combines user shortcode attributes with known attributes and fills in defaults when needed. The result will contain every key from the known attributes, merged with values from shortcode attributes.”
This allows us to specify some default attributes for our shortcode. In our case, our default 'post_author'
for the submitted post will be the currently logged-in user or 1
, the default submitted post’s status will be 'pending'
and the default submitted post’s 'post_type'
will be 'post'
. If I wanted, I can change all those attributes by modifying the shortcode in my content like so: [cmb-frontend-form post_author=2 post_status="draft" post_type="page"]
.
Next, let’s add our CMB2 form to the shortcode output. We’re going to ‘zoom in’ a bit with our snippet. At the end, we’ll put it all together.
/** * Handle the cmb-frontend-form shortcode * * @param array $atts Array of shortcode attributes * @return string Form html */ function wds_do_frontend_form_submission_shortcode( $atts = array() ) { // ... Previous function code omitted for brevity // Initiate our output variable $output = ''; // Get our form $output .= cmb2_get_metabox_form( $cmb, $object_id, array( 'save_button' => __( 'Submit Post', 'wds-post-submit' ) ) ); return $output; } add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );
With that new line, we’re actually retrieving the CMB2 form markup. The parameters we pass to the cmb2_get_metabox_form
function include the $cmb
object we just retrieved, the fake post id we created, and an array of arguments we want to override in the cmb2_get_metabox_form
function. In our case, we only want to override the text of the 'save_button'
to more accurately reflect what the button will be doing.
So now we have a form on the page, which is pretty exciting. At this point, you may want to take some time to add some custom styles to your theme’s stylesheet to make the form look how you want. But there is a key flaw: the form will not do anything when we submit. Our 'save_fields' => false
ensures that. So let’s go ahead and create our submission handler.
/** * Handles form submission on save * * @param CMB2 $cmb The CMB2 object * @param array $post_data Array of post-data for new post * @return mixed New post ID if successful */ function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) { // If no form submission, bail if ( empty( $_POST ) ) { return false; } // check required $_POST variables and security nonce if ( ! isset( $_POST['submit-cmb'], $_POST['object_id'], $_POST[ $cmb->nonce() ] ) || ! wp_verify_nonce( $_POST[ $cmb->nonce() ], $cmb->nonce() ) ) { return new WP_Error( 'security_fail', __( 'Security check failed.' ) ); } if ( empty( $_POST['submitted_post_title'] ) ) { return new WP_Error( 'post_data_missing', __( 'New post requires a title.' ) ); } // Do WordPress insert_post stuff return $new_submission_id; }
Our post-submission handler function, wds_handle_frontend_new_post_form_submission
‘ takes two arguments, a CMB2 object, and an optional array of post data for the inserted post.
The first step is to check if the form has even been submitted. If not, we bail out early. If so, then we verify that all the security pieces are in place as well as the required post data. We’re requiring the user to at least submit a title for their post. If all goes well, we’re now ready to create our new post.
Now, let’s leverage a new method, get_sanitized_values
, to sanitize the array of fields data submitted, We’ll pass it the $_POST
variable. Once the values have been properly sanitized, let’s set our new post’s title and content from those fields and insert it!
/** * Handles form submission on save * * @param CMB2 $cmb The CMB2 object * @param array $post_data Array of post-data for new post * @return mixed New post ID if successful */ function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) { // ... Previous function code omitted for brevity // Fetch sanitized values $sanitized_values = $cmb->get_sanitized_values( $_POST ); // Set our post data arguments $post_data['post_title'] = $sanitized_values['submitted_post_title']; unset( $sanitized_values['submitted_post_title'] ); $post_data['post_content'] = $sanitized_values['submitted_post_content']; unset( $sanitized_values['submitted_post_content'] ); // Create the new post $new_submission_id = wp_insert_post( $post_data, true ); // If we hit a snag, update the user if ( is_wp_error( $new_submission_id ) ) { return $new_submission_id; } return $new_submission_id; }
Ok, Let’s handle the featured image and custom post meta for the new post:
/** * Handles form submission on save * * @param CMB2 $cmb The CMB2 object * @param array $post_data Array of post-data for new post * @return mixed New post ID if successful */ function wds_handle_frontend_new_post_form_submission( $cmb, $post_data = array() ) { // ... Previous function code omitted for brevity // If we hit a snag, update the user if ( is_wp_error( $new_submission_id ) ) { return $new_submission_id; } /** * Other than post_type and post_status, we want * our uploaded attachment post to have the same post-data */ unset( $post_data['post_type'] ); unset( $post_data['post_status'] ); // Try to upload the featured image $img_id = wds_frontend_form_photo_upload( $new_submission_id, $post_data ); // If our photo upload was successful, set the featured image if ( $img_id && ! is_wp_error( $img_id ) ) { set_post_thumbnail( $new_submission_id, $img_id ); } // Loop through remaining (sanitized) data, and save to post-meta foreach ( $sanitized_values as $key => $value ) { update_post_meta( $new_submission_id, $key, $value ); } return $new_submission_id; }
You can see we’re using a helper function, wds_frontend_form_photo_upload
, (which is just a wrapper for media_handle_upload
). It’s not directly related to CMB2, so I won’t go over it here, but I’ll include it in the final code snippet.
After we upload our image to the new post, if all went well, we’ll make that image the post’s featured image (set_post_thumbnail
).
And finally, we’ll loop through the rest of the sanitized field values and save them as post meta.
Now that we have our custom save handler, we need to incorporate it back into our shortcode handler function, wds_do_frontend_form_submission_shortcode
.
/** * Handle the cmb-frontend-form shortcode * * @param array $atts Array of shortcode attributes * @return string Form html */ function wds_do_frontend_form_submission_shortcode( $atts = array() ) { // ... Previous function code omitted for brevity // Initiate our output variable $output = ''; // Handle form saving (if form has been submitted) $new_id = wds_handle_frontend_new_post_form_submission( $cmb, $atts ); if ( $new_id ) { if ( is_wp_error( $new_id ) ) { // If there was an error with the submission, add it to our ouput. $output .= '<h3>' . sprintf( __( 'There was an error in the submission: %s', 'wds-post-submit' ), '<strong>'. $new_id->get_error_message() .'</strong>' ) . '</h3>'; } else { // Get submitter's name $name = isset( $_POST['submitted_author_name'] ) && $_POST['submitted_author_name'] ? ' '. $_POST['submitted_author_name'] : ''; // Add notice of submission $output .= '<h3>' . sprintf( __( 'Thank you %s, your new post has been submitted and is pending review by a site administrator.', 'wds-post-submit' ), esc_html( $name ) ) . '</h3>'; } } // Get our form $output .= cmb2_get_metabox_form( $cmb, $object_id, array( 'save_button' => __( 'Submit Post', 'wds-post-submit' ) ) ); return $output; } add_shortcode( 'cmb-frontend-form', 'wds_do_frontend_form_submission_shortcode' );
You can see, right after we intiate our $output
variable, and just before we use cmb2_get_metabox_form
to retrieve our form markup, we’re using our new wds_handle_frontend_new_post_form_submission
function to save any submitted post entries. If it saves the post, it will return the new post ID, but if it hit a snag, will return a WP_Error
object.
So if we got a response back from our save handler, we’re going to output a message to the user. If the submission process hit a snag, the user will be alerted with the proper error message (and otherwise be given a ‘success’ message). These messages get appended to the $output
variable before the form markup so that they will show up at the top of the form.
One issue with this method is that if user hits refresh after submitting post, a new post will continue to be submitted. You should probably either check for an existing post with the submitted data BEFORE doing wp_insert_post
, or implement a redirect when the form is submitted, but I’ll have to cover that another time. (Update 5/23/15: The snippet has been updated to now redirect on successful submission, woot!)
And there you have it! A flexible front-end submission form that you can use to generate new posts (or other post-types). You can use this for all kinds of ideas. Maybe you want a movie review site–just change the registered fields for your form to reflect the kind of data you would want for that review. Title, content, rating, submitter’s name/email, etc. You can see there is a lot of flexibility!
You can find the entire snippet at the CMB2 Snippet Library. If you haven’t yet checked out the CMB2 Snippet Library, you definitely should! There are a lot of tips and tricks (like this one!) throughout.
The post Use CMB2 to Create a New Post Submission Form appeared first on WebDevStudios.
]]>The post Create a Filterable Portfolio with WordPress and Jetpack appeared first on WebDevStudios.
]]>Tools you’ll need:
Notes on Isotope:
The great thing about Isotope is that it is GPL licensed for Open Source projects. However, if your site is a commercial project, I would highly recommend purchasing a commercial license. This will help the developer to continue to support Isotope.
Step 1:
The first thing we need to do is setup Jetpack and enable the ‘Portfolio Custom Content Type’. To do this, while in your WordPress dashboard, go to Jetpack > Settings
and activate the ‘Custom Content Type’ module.
Next, we need to make sure your theme supports the Portfolio Custom Content Type. To do this, we need to add the following code to your theme, during after_setup_theme
:
add_theme_support( 'jetpack-portfolio' );
Once that is added, you’ll see that a new menu item has been added to your WordPress dashboard:
Step 2:
We’ll need to properly enqueue all needed files for your theme. You can add this to wherever your theme is already enqueuing files.
wp_register_script( 'wds-isotope', get_template_directory_uri() . '/js/isotope.pkgd.min.js', array( 'jquery' ), $version, true ); wp_register_script( 'wds-portfolio', get_template_directory_uri() . '/js/wds-portfolio.js', array( 'jquery' ), $version, true ); if ( is_page_template( 'homepage-template.php' ) ) { wp_enqueue_script( 'wds-isotope' ); wp_enqueue_script( 'wds-portfolio' ); }
Notice that we are using a conditional to only enqueue our scripts on any page using our ‘homepage-template.php’ page template. This is so that we are not enqueuing our JS files site wide. Now that they have been enqueued, download Isotope and add it to your theme’s JavaScript folder. Make sure this folder is named ‘js’. If not, you’ll need to update the above script and change the location of where it is being enqueued. Once you have added the Isotope file to your JS folder, create a new file and name it ‘wds-portfolio.js’. Add the following jQuery code and save the file into your ‘js’ folder.
/** * Portfolio functions */ ( function( $ ) { $( window ).load( function() { // Portfolio filtering var $container = $( '.portfolio' ); $container.isotope( { filter: '*', layoutMode: 'fitRows', resizable: true, } ); // filter items when filter link is clicked $( '.portfolio-filter li' ).click( function(){ var selector = $( this ).attr( 'data-filter' ); $container.isotope( { filter: selector, } ); return false; } ); } ); } )( jQuery );
Step 3:
Now that we have enqueued your files, let’s create a page template that is specific for your portfolio, and one to show everything on the homepage.
Create a new file and name it ‘homepage-template.php’. Copy and paste the following code:
<?php /* * Template Name: Homepage Template * * @package wds_portfolio */ get_header(); ?> <div id="page" class="hfeed site"> <div id="main" class="site-main"> <?php if ( ! get_theme_mod( 'wds_portfolio_hide_portfolio_page_content' ) ) : ?> <?php while ( have_posts() ) : the_post(); ?> <?php the_title( '<header class="page-header"><h1 class="page-title">', '</h1></header>' ); ?> <div class="page-content"> <?php the_content(); wp_link_pages( array( 'before' => '<div class="page-links"><span class="page-links-title">' . __( 'Pages:', 'wds_portfolio' ) . '</span>', 'after' => '</div>', 'link_before' => '<span>', 'link_after' => '</span>', ) ); ?> </div><!-- .page-content --> <?php endwhile; // end of the loop. ?> <?php endif; ?> <div class="portfolio-filter"> <ul> <li id="filter--all" class="filter active" data-filter="*"><?php _e( 'View All', 'wds_portfolio' ) ?></li> <?php // list terms in a given taxonomy $taxonomy = 'jetpack-portfolio-type'; $tax_terms = get_terms( $taxonomy ); foreach ( $tax_terms as $tax_term ) { echo '<li class="filter" data-filter=".'. $tax_term->slug.'">' . $tax_term->slug .'</li>'; } ?> </ul> </div> <div class="portfolio"> <?php if ( get_query_var( 'paged' ) ) : $paged = get_query_var( 'paged' ); elseif ( get_query_var( 'page' ) ) : $paged = get_query_var( 'page' ); else : $paged = -1; endif; $posts_per_page = get_option( 'jetpack_portfolio_posts_per_page', '-1' ); $args = array( 'post_type' => 'jetpack-portfolio', 'paged' => $paged, 'posts_per_page' => $posts_per_page, ); $project_query = new WP_Query ( $args ); if ( post_type_exists( 'jetpack-portfolio' ) && $project_query -> have_posts() ) : while ( $project_query -> have_posts() ) : $project_query -> the_post(); get_template_part( 'content', 'portfolio' ); endwhile; wds_portfolio_paging_nav( $project_query->max_num_pages ); wp_reset_postdata(); else : ?> <section class="no-results not-found"> <header class="page-header"> <h1 class="page-title"><?php _e( 'No Project Found', 'wds_portfolio' ); ?></h1> </header><!-- .page-header --> <div class="page-content"> <?php if ( current_user_can( 'publish_posts' ) ) : ?> <p><?php printf( __( 'Ready to publish your first project? <a href="%1$s">Get started here</a>.', 'wds_portfolio' ), esc_url( admin_url( 'post-new.php?post_type=jetpack-portfolio' ) ) ); ?></p> <?php else : ?> <p><?php _e( 'It seems we can’t find what you’re looking for. Perhaps searching can help.', 'wds_portfolio' ); ?></p> <?php endif; ?> </div><!-- .page-content --> </section><!-- .no-results --> <?php endif; ?> </div><!-- .portfolio --> <?php get_footer(); ?>
Next, we’ll need to create a ‘content-portfolio.php’ template file so we know what portfolio item to grab. Create a new file and name it ‘content-portfolio.php’. Here is our code for the content file:
Edit: See Stefan’s comment for necessary change for line 19.
<?php /** * The template for displaying Projects on index view * * @package wds-portfolio */ // get Jetpack Portfolio taxonomy terms for portfolio filtering $terms = get_the_terms( $post->ID, 'jetpack-portfolio-type' ); if ( $terms && ! is_wp_error( $terms ) ) : $filtering_links = array(); foreach ( $terms as $term ) { $filtering_links[] = $term->slug; } $filtering = join( ", ", $filtering_links ); ?> <article id="post-<?php the_ID(); ?>" <?php post_class( $filtering ); ?>> <a href="<?php the_permalink(); ?>" rel="bookmark" class="image-link" tabindex="-1"> <?php if ( '' != get_the_post_thumbnail() ) : ?> <?php the_post_thumbnail( 'wds-portfolio-img' ); ?> <?php endif; ?> </a> </article><!-- #post-## --> <?php endif;
What this code does is include the page content of whatever the page you create that uses this page template. We are also grabbing all content that is published with the Portfolio Custom Content Type.
The following sections are necessary for filtering our portfolio items.
<div class="portfolio-filter"> <ul> <li id="filter--all" class="filter active" data-filter="*"><?php _e( 'View All', 'wds_portfolio' ) ?></li> <?php // list terms in a given taxonomy $taxonomy = 'jetpack-portfolio-type'; $tax_terms = get_terms( $taxonomy ); foreach ( $tax_terms as $tax_term ) { echo '<li class="filter" data-filter=".'. $tax_term->slug.'">' . $tax_term->slug .'</li>'; } ?> </ul> </div>
The above code block grabs all taxonomy terms from the Portfolio Custom Content Type in a list. This will allow us to filter our portfolio items.
The next block of code will display our portfolio posts:
<div class="portfolio"> <?php if ( get_query_var( 'paged' ) ) : $paged = get_query_var( 'paged' ); elseif ( get_query_var( 'page' ) ) : $paged = get_query_var( 'page' ); else : $paged = 1; endif; $posts_per_page = get_option( 'jetpack_portfolio_posts_per_page', '-1' ); $args = array( 'post_type' => 'jetpack-portfolio', 'paged' => $paged, 'posts_per_page' => $posts_per_page, ); $project_query = new WP_Query ( $args ); if ( post_type_exists( 'jetpack-portfolio' ) && $project_query -> have_posts() ) : while ( $project_query -> have_posts() ) : $project_query -> the_post(); get_template_part( 'content', 'portfolio' ); endwhile; wds_portfolio_paging_nav( $project_query->max_num_pages ); wp_reset_postdata(); else : ?> <section class="no-results not-found"> <header class="page-header"> <h1 class="page-title"><?php _e( 'No Project Found', 'wds_portfolio' ); ?></h1> </header><!-- .page-header --> <div class="page-content"> <?php if ( current_user_can( 'publish_posts' ) ) : ?> <p><?php printf( __( 'Ready to publish your first project? <a href="%1$s">Get started here</a>.', 'wds_portfolio' ), esc_url( admin_url( 'post-new.php?post_type=jetpack-portfolio' ) ) ); ?></p> <?php else : ?> <p><?php _e( 'It seems we can’t find what you’re looking for. Perhaps searching can help.', 'wds_portfolio' ); ?></p> <?php endif; ?> </div><!-- .page-content --> </section><!-- .no-results --> <?php endif; ?> </div><!-- .portfolio -->
The key to the above block are the class names. The ‘portfolio’ class is being called in our wds-portfolio.js file. This tells our jQuery that all posts under this class are to be filtered.
The above code block also includes a specific query that grabs all posts from our ‘Portfolio Custom Content Type’.
Step 4:
Now it’s time to add some styling. The following styling will cover the filter list items and your portfolio items. Feel free to adjust it to match the look of your website.
.portfolio-filter { font-size: 14px; font-size: 1.4rem; overflow: hidden; text-transform: uppercase; } .portfolio-filter ul { margin: 0 0 2em; padding: 0; text-align: center; width: 100%; } .portfolio-filter ul li { background: #f0f0f0; color: #999; cursor: pointer; display: inline-block; list-style-type: none; margin: 0 1em 1em 0; padding: .2em .5em; text-align: center; } .portfolio-filter ul li:first-child { margin-right: .8em; } .portfolio-filter ul li:focus, .portfolio-filter ul li:hover { color: #000; } .portfolio .type-jetpack-portfolio { float: left; margin: 0 -1px -1px 0; width: 100%; } @media screen and (min-width: 480px) { .portfolio .type-jetpack-portfolio { max-width: 100%; min-height: inherit; } } @media screen and (min-width: 768px) { .portfolio .type-jetpack-portfolio { max-width: 239px; max-height: 180px; } } .portfolio .type-jetpack-portfolio img { display: block; } .portfolio .type-jetpack-portfolio:hover { background: #000; opacity: .6; transition: all .4s ease-in-out; -webkit-transition: all .4s ease-in-out; -moz-transition: all .4s ease-in-out; -o-transition: all .4s ease-in-out; }
Step 5:
Now that all code and files are added to our theme, let’s go back into our WordPress dashboard to start publishing items in our Portfolio Custom Content Type. When publishing a new portfolio, be sure to add ‘Project Types’ to your posts. This will categorize your posts and your page will show the project types as fitlerable items–which will help in filtering your portfolio.
Added Bonus!
If you are running into any issues, we have included a free theme that you can download. You can compare your code with the files with this theme. This will help you debug any possible issues.
Like our free theme?
It’s on GitHub! Pull requests are welcome to help improve our theme.
Further Theme Development Resources
WordPress Theme Development Standards
All WebDevStudios’ posts on Theme Development and Designing
The post Create a Filterable Portfolio with WordPress and Jetpack appeared first on WebDevStudios.
]]>The post Attaching Posts with CMB2 for WordPress appeared first on WebDevStudios.
]]>Every once in a while you may find yourself needing to attach various pieces of content to various other pieces of content within your site. Maybe you want to display a list of related content on a single post but you’re extremely particular about which posts should display as related. Maybe you have a special page template which needs to display various posts as sections or chapters on a single page.
Whatever the case may be, we always seem to find ourselves needing to attach one type of content to another type of content. With CMB2 Attached Posts, we give you that ability.
Sure, there are plugins that will do this for you. Here at WebDevStudios we’ve used Posts 2 Posts quite a bit which is a wonderful plugin allowing you to pair content from one post type to another. Why use CMB2, then? Well, why not?? We want to make CMB2 be and do all that it can, and when the need for relationships between post types came up it seemed like the perfect time to integrate this functionality directly into CMB2.
First things first, you’ll need to check out the CMB2 Attached Posts repo. You’ll drop those files right into your /mu-plugins/ directory, which will register the new CMB2 field type and enqueue the necessary JavaScript and CSS files in the dashboard.
So now that you’ve got that taken care of, let’s go!
First, we need to add the field like we would with any other CMB2 field:
As an added bonus, our own Justin Sternberg just made an update to this functionality on line 15 above. You can now specify the number of posts you want to pull in when using this field type!
Once you’ve got all of your options and parameters set, you’ll wind up with something like this:
Simple, right? This is going to look for posts and display this field ONLY on pages as indicated by the ‘object_types’ value above.
Once you have that in place, you’re ready to start draggin’ and droppin’!
When you update the post you’re editing, you’ll now have these post IDs saved in an array for you to use on the front-end. By rearranging posts, you’re able to rearrange them in the array itself; so, if you need to display this content in a specific order then drag and drop is going to be your best buddy.
Now that you have a nifty little array, you can do something like this do bring these babies to life:
The array stores the ID of each post, so the world is really your stinky little oyster as far as what you want to do with these posts. It’s all up to you!
You can grab CMB2 here: https://github.com/WebDevStudios/CMB2
You can grab CMB2 Attached Posts here: https://github.com/WebDevStudios/cmb2-attached-posts/
The post Attaching Posts with CMB2 for WordPress appeared first on WebDevStudios.
]]>