Blog posts under the WordPress tag https://webdevstudios.com/tags/wordpress/ WordPress Design and Development Agency Mon, 15 Apr 2024 16:02:09 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://webdevstudios.com/wp-content/uploads/2022/07/cropped-wds-icon.white-on-dark-60x60.png Blog posts under the WordPress tag https://webdevstudios.com/tags/wordpress/ 32 32 58379230 Best Practices for WordPress Website Image Optimization https://webdevstudios.com/2018/08/16/wordpress-website-image-optimization/ https://webdevstudios.com/2018/08/16/wordpress-website-image-optimization/#comments Thu, 16 Aug 2018 16:00:24 +0000 https://webdevstudios.com/?p=18963 The most significant consideration for building or maintaining a website these days is speed. Ensuring that your site moves quickly and supports speeds down to mobile 3G is vital in providing the best possible user experience (UX) to the largest percentage of your target demographic. The guiltiest speed hogs are network requests dedicated to fetching Read More Best Practices for WordPress Website Image Optimization

The post Best Practices for WordPress Website Image Optimization appeared first on WebDevStudios.

]]>
The most significant consideration for building or maintaining a website these days is speed. Ensuring that your site moves quickly and supports speeds down to mobile 3G is vital in providing the best possible user experience (UX) to the largest percentage of your target demographic. The guiltiest speed hogs are network requests dedicated to fetching images and video media. One of the best ways to improve UX and guarantee site speed is to consistently practice techniques for WordPress website image optimization. If you think about images as a percentage of your overall website’s footprint, a few bytes here and there will certainly add up. By optimizing images, you can reduce the overall impact of your website’s footprint, page size, and increase load speeds.

Image optimization is just that, optimizing, compressing, scaling, resizing, or changing image formats for the web. The general idea is to reduce how many requests you have to make or how large those requests are in the first place. Google uses a term called “time to first meaningful paint,” which is when “the user feels that the primary content of the page is visible.”  The faster users get to the information they’re looking for, the better off you will be.

There are many ways that WordPress website owners can improve load speeds. Some techniques can be used before an image makes it to your website and some by directly modifying your server configuration and the way that the site handles images. Let’s explore both.

Compression and Optimization

Uncompressed images can contain additional bulk that should be removed when you save your files for the web, once you already have an image, or once it’s already on your website. You can manually optimize images in programs like Photoshop, Pixelmator, or Affinity Photo. By ensuring that you’re selecting the proper image format, quality, and a few other settings, you’ll be well on your way to improving your website. Photoshop, for example, has a default preset called “JPEG High” that takes care of the bulk of the optimization for you which is probably a safe bet nine times out of ten.

We want to make sure that our image quality is set as low as possible without destroying the integrity of the image. Select “Optimized” to strip out unnecessary data from the image and further compress it. I typically find that 60 is a good quality. Much lower than that and you’ll start to see major image degradation.

A slight note on quality: by default, WordPress uses the jpeg_quality hook to set uploaded JPEG/JPG image quality to 60. You may find that images that seemed clear initially are a little fuzzy after upload. So, play around with the quality vs size ratios to see what works best for you.

JPG vs PNG vs All the Other Formats

JPG/JPEG and PNG are by far the most popular image formats you’ll see online. There are plenty of other image formats like GIFs and newer formats like JPEG 2000, JPEG XR, APNG, but you don’t need to worry about all that. Most of them aren’t widely supported by major browsers or don’t provide any additional benefit. So if you see those online, stick to the basics.

JPG/JPEGs are “lossy,” meaning that unnecessary data is removed during compression or when you save a file as a JPEG. Most of the time this doesn’t matter especially for photos, but if your image has text in it or requires transparency, you might be better served with a PNG.

PNGs support transparency and have a higher level of quality because they’re considered “lossless,” which means that no data is lost during compression. Graphics with details, like text, or that require transparent backgrounds/elements are perfect for a PNG. However, because none of that information is removed or lost, it will end up being a larger file than a JPEG. Play around with those formats and see. Sometimes the difference in size is negligible, and sometimes you’ll see a PNG at two or three times the size of a comparable JPEG.

So which is right for you?

If you’re saving an image to be a background element, design element, photograph, or something that doesn’t need to render super-detailed elements, JPEG is the way to go. If you’re dealing with text over images, transparency, or need super-detailed or crisp edges, a PNG is the way to go.

What about SVGs?

Keep in mind that not everything needs to be an image. SVGs are super lightweight because they are generated by mathematically rendering shapes from just a few lines of text. SVG supports transparency, animation, and can be colored programmatically unlike raster images. SVGs, because they are vector shapes, can be as large or as small as you want without ever losing quality.

Not everything can be an SVG, though. Only elements that don’t rely on pixels (photographs) can be an SVG. Simple icons and shapes or your logo are prime examples of an SVG use case. Because it’s code rendered by your browser, it’s super lightweight and doesn’t bog down your server with beefy network requests.

Size

I’m not necessarily talking about file size here. If we’ve optimized our images properly and chosen the right file format, we should be in good shape as the file size is concerned. I’m talking about the dimensions of the image. If the image you’re using will take up approximately 300 pixels x 200 pixels on screen, you don’t need to be using an image that is ten times the size, which happens more often than you’d think. Website platforms like WordPress will automatically generate many different sizes to allow users to select a more reasonable image size instead of always embedding “full size” images.

Purposefully selecting images and image sizes for sections of your website is vital toward improving your website’s performance. If you don’t have a desktop program, there are plenty of websites online to help you out like Image Resize or Pixlr Editor with similar size and optimization capabilities.

Metadata

All image formats contain additional metadata by default—data that describes the file, such as a title, copyright information, image ratings, GPS location data, file and dimension sizes, how, where, or when the file was edited, etc. Most of the time, these are necessary for the web unless you’re protecting your property intellectually, but that’s for another day.

Selecting the “optimized” function in Photoshop, or selecting “None” in the Metadata dropdown, will further reduce your file sizes. It is often helpful when the image is only intended to be used as a design element, or your source attributions are coded into the website itself.

Scale Images and Retina Devices

You want to select the right image for the job, but you also want to make sure that you’re providing some flexibility for desktop, tablet, and mobile devices just the same. A desktop website view might have an image of 600 pixels x 800 pixels in size, but for a phone that might be overkill, especially on slower internet speeds or older phones.

WordPress provides the ability to display images for each browser size dynamically using the `srcset` property of an image. So the image that is loaded on mobile devices is different from the image on tablet or desktop devices. They are incrementally smaller to ensure a faster load time, all handled within the image call dynamically for you.

Apple devices and devices with higher DPI screens tend to expect higher quality images in order to render clearly. The general rule is to serve images up to two times the maximum display size on all devices—desktop, tablet, and mobile at 600 pixels x 400 pixels, 400 pixels x 300 pixels, or 300 pixels x 200 pixels, respectively. Make sure that all images are optimized for the device, but also not larger than the maximum use case.

As long as you never use an image that’s larger than you need, you should be a-okay. Imsanity, a plugin for WordPress, also provides the ability bulk size or resize images for your browser more efficiently, rather than having to update your theme or configure any servers.

Caching

If you’re running any site audit tool, you might see “Leverage Browser Caching” come up as something to address. Storing images within the browser to quickly grab and render when your website loads, instead of having to make a network request to retrieve it, will reduce the load times and page size exponentially. Browser caching is a bit more complicated or may require an engineer to help optimize as it typically needs a server configuration to handle Cache-Control headers and Expires headers.

If your images don’t change very frequently, or at all, caching your media would provide almost instant access to your assets without needing to make a full network request every time, and websites that continually changes or doesn’t get high volume traffic may not necessarily benefit much from this approach. Plugins like Leverage Browser Caching may provide a direct ability to edit your headers but should be used with caution. Keep in mind, that this plugin is only intended for use with Apache Servers. Several hosting companies out there rely on NGINX, like WPEngine, so this would require a more dev-centered-server-configuration approach.

Also remember, however, that when caching is enabled you may be continuously needing to ask your users or clients to “flush their caches” or need to flush site caches manually.

CDN

A Content Delivery Network (CDN) allows you to store cached content which serves up content directly to your users rather than waiting for requests from your website instantly. A CDN’s obvious benefit is speed for all users no matter where they might be on the planet. A CDN provides proxy servers with your content for quick and reliable access regardless of location. Without a CDN, a server in New York, for example, would load much faster to someone in Ohio than it would to someone in New Zealand. With a CDN, those proxy servers relay information based on location to ensure speed.

CDNs, along with speed and caching, provide additional security benefits which are especially helpful if your content is in any way sensitive.

Additional Services

There are many other services out there to help optimize images or serve them up more efficiently. Two good examples are Smush Image Compression and Kraken Image Optimizer. Both are paid services that allow you to compress and optimizes images already in your WordPress media library, as well as those uploaded thereafter.

For optimization on-the-fly, there is Cloudinary, which allows you to upload, manage, optimize, and store images. Images are delivered via CDN and provide responsive assets as needed automatically. Cloudinary also allows you to modify, define image sizes, add effects, or change alignment on-the-fly by using URL query string options, which is a nice additional feature but not always necessarily related to image optimization.

Don’t Forget the Frontend

I’ve been talking about image optimization in a technical sense, but we can also think about optimization in the sense that organizing images, calling them correctly, and using best practices for the web can improve website visibility for SEO and avoid potential accessibility pitfalls. Ensure all images have unique alt text, something that is almost always neglected in my experience but is one of the more critical aspects for accessibility and SEO.

All unique file names should then have unique alt text. If an image is decorative, the proper way to present them is as a background image in your CSS, but if it must be inline and is purely decorative, it should at least define the alt tag as empty with the role attribute of presentation.

<img role="presentation" alt="" />

An alt tag should directly reference the image that is being displayed. If an image src is broken or unavailable, or a visually impaired individual is using your website, they should know what they’re looking at by its description. image-1 is an unacceptable alt text.

If you find yourself needing to provide an additional explanation about the image, you may use the longdesc="" attribute and give a location or file for reference, which is helpful for diagrams, etc.

Summation

It’s not the easiest thing in the world to assess and optimize your media, but you will find that once you do, your users will thank you by staying on your website longer. After all, a large percentage of website visitors will up and leave if they don’t get the information they need from in under three seconds. That alone makes WordPress website image optimization worth the effort. However, if you still need help, contact us for assistance.

The post Best Practices for WordPress Website Image Optimization appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2018/08/16/wordpress-website-image-optimization/feed/ 2 18963
The Journey to 1.0 — WDS Blocks and WordPress Gutenberg https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/ https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/#comments Tue, 22 May 2018 16:00:02 +0000 https://webdevstudios.com/?p=18432 WordPress made a big leap into the world of modern web development with its announcement of Gutenberg last year—reimagining what WordPress is and can be by providing a new interface for creating content built partially on the React.js platform. Since it will most likely be rolled into Core sometime mid to late 2018, WebDevStudios (WDS) Read More The Journey to 1.0 — WDS Blocks and WordPress Gutenberg

The post The Journey to 1.0 — WDS Blocks and WordPress Gutenberg appeared first on WebDevStudios.

]]>
WordPress made a big leap into the world of modern web development with its announcement of Gutenberg last year—reimagining what WordPress is and can be by providing a new interface for creating content built partially on the React.js platform. Since it will most likely be rolled into Core sometime mid to late 2018, WebDevStudios (WDS) is looking toward the future. Enter WDS Blocks.

As of May 2018, WordPress runs ~30.7% of the internet. For better or worse, it’s going to be hard to ignore the impact Gutenberg will have on the web when it officially launches. The new feature is being teased to the public now, and though it’s nowhere near a stable final release, its adoption rate is growing exponentially and WDS needed a game plan to ensure we’re not behind the curve.

Pre-Planning

In August 2017, during one of our weekly leadership calls, the topic of Gutenberg was brought up. Everyone thought the project was a bit premature to start building on top of. We discussed the possible future implications but weren’t ready to give the idea of building our own custom Gutenberg blocks any serious attention. Gutenberg had just barely been announced, and like many suggested features, its future was uncertain. So it was tabled for several months.

Four months later, WDS named Greg Rickaby to the position of Director of Engineering, and by this time, Gutenberg had become the biggest topic in the WordPress space—there was no ignoring it.

Greg spoke with Cristina Holt, our Director of Project Management, and the initial thoughts were to approach Gutenberg just like a client project and pitch it to our CEO, Brad Williams, and put the full weight of our talented developers behind the project.

“First we needed to train our engineers and project managers,” explains Greg. “Second, we had to come up with a development plan and schedule, then finally put together a project team. It took a few conversations with leadership, but Brad and Lisa eventually agreed to let us move forward with the ‘WDS Gutenberg Project.’”

Once the project was approved, Cristina and Greg assembled a team which would be led by Lead Frontend Engineer, Corey Collins, and Lead Backend Engineer, Kellen Mace. The engineering team was rounded out by Jo Murgel, Eric Fuller, and Will Schmierer.

Our first release was to replicate or replace components such as Heroes, Cards, and Calls-To-Action. These are components that are commonly used by our clients on typical website projects and are already included in our starter theme, wd_s. We call these “Global Content Blocks” and they’re powered by Advanced Custom Fields’ “Flexible Content” feature.

The Training Plan

Zac Gordon has been leading the charge on educating WordPress developers since Matt Mullenweg so famously said, “Learn JavaScript, deeply.” We purchased Zac’s Gutenberg course (now called WordPress Block Editor) over at https://javascriptforwp.com/. After all, the best way to learn anything is to immerse yourself.

Each of our engineers was scheduled some time to take the course. Once they completed their training, they could get spun up on WDS Blocks.

The Development Plan

While training, Kellen Mace had read about Ahmad Awais’s Create Guten Block on Github, which bills itself as a zero-configuration toolkit for spinning up Gutenberg blocks. He proposed using it to provide the foundation for our WDS Blocks project. Finding this toolkit was honestly a relief, as the thought of scaffolding the project and configuring Webpack from scratch wasn’t something any of us really wanted to do.

We also needed to provide greater advanced settings for each block including the ability to change the background decoration (image, color, video), add some visual flair (animate.css classNames), and a few other options to provide greater control to users beyond what is inherently available from WordPress out of the box.

Our blocks needed to provide a simple and clean backend experience with a fairly generic theme-friendly frontend render. For release 1.0 we ended up working toward a hero, call to action area, recent posts grid, manual-select related posts grid, a dual-column layout, and a gist embed. Since then we’d added several updates to these blocks and a users grid block with room to grow going forward.

The Result

Like most stories, the journey is far more interesting than the end result. We learned a lot about what Gutenberg can and can’t do, why they chose React.js, and why they added an abstraction layer.

Utilizing Gutenberg’s Inspector Control tools, we were able to create a set of Background, Text, and Other Options that we could easily replicate across all of our blocks. We even wrote about it so we could share what we learned and how we were able to keep our code DRY in order for our options to be as easy as possible to implement in new blocks.

Despite the insistence from WordPress themselves that aMultiSelect component wasn’t necessary, Issue #1044, we found the use case for such a component important. So we built one as a part of our recent posts and users grid blocks.

During the rest of our development, we found ourselves needing to reuse bits and pieces throughout our blocks. In addition to creating seven new blocks (plus a default block to copy from), we also created 10 new reusable components! These cover Options noted above, post search, and MultiSelect amongst others. Being able to create standalone components, and control their output at the component level, helps us keep a clean and consistent codebase across all of our blocks.

Based on the way Gutenberg is built and how it renders markup to post_content there was a problem with creating dynamic or non-static blocks that needed to be worked out. More on that at the WDS Blog: WordPress Gutenberg — Arrays, Attributes, and the Fundamental Flaw – WebDevStudios.

Initially, our plan was to render all of our blocks via PHP because we ran into issues when updating the markup for blocks. For instance, when rendering in JSX, if you were to add a className to your block container, you would receive a popup in the post editor informing you that your block was no longer valid, and the new className would not be present on the front-end. After some digging, we were able to realize that Gutenberg’s deprecated function was exactly what we needed. This allows for you to make changes to your block, to have those updates visible on both the backend and frontend of your site and to avoid the “invalid markup” popup after changing your block’s markup. You can read more about Deprecated Blocks in the Gutenberg Handbook.

Finally, WordPress provides a feature for styling both the front and backend render. Before Gutenberg existed, you’d have a simple text-based WYSIWYG editor with no real indication of how the content might look on the front end. Appending theme styles to a WordPress Gutenberg Block has never been easier, with its shared styles allowing for a user experience which more closely resembles that of the frontend.

The Future

This is just the beginning. Every release of Gutenberg unveils changes in functionality, bug fixes and deprecated features forcing us to keep up with testing and look for additional functionality that can improve the user experience.

We plan to use WDS Blocks as a starting point for new website projects, providing many of the common blocks that our clients will likely need, then add to those any project-specific blocks.

We’ve opened the repo to the public and encourage everyone to get involved with the project. We welcome your help as we work to build out a library of powerful and useful blocks that take full advantage of WordPress’ new editing experience.

The post The Journey to 1.0 — WDS Blocks and WordPress Gutenberg appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/feed/ 1 18432
Evolution of CSS: Becoming a CSS Composer https://webdevstudios.com/2017/02/28/evolution-css-becoming-css-composer/ https://webdevstudios.com/2017/02/28/evolution-css-becoming-css-composer/#respond Tue, 28 Feb 2017 17:00:25 +0000 http://webdevstudios.com/?p=16183 I have been coding CSS almost daily for over 10 years. I don’t say that to try and establish supremacy, but merely to give some context to the subsequent observations. There are quite a few days I still feel more like a CSS poser. Keeping with the Non-Traditional Traditions I received my degree from a Read More Evolution of CSS: Becoming a CSS Composer

The post Evolution of CSS: Becoming a CSS Composer appeared first on WebDevStudios.

]]>
I have been coding CSS almost daily for over 10 years. I don’t say that to try and establish supremacy, but merely to give some context to the subsequent observations. There are quite a few days I still feel more like a CSS poser.

Keeping with the Non-Traditional Traditions

I received my degree from a private liberal arts college, but only after a large and intentional vacation from formal education after high school. The college had a non-traditional, experimental program that was typically advertised toward “returning adults,” and this is where I chose to finally continue my formal education. It allowed me to not necessarily have a major, but a “focus” in communications, and specifically, an “interdisciplinary program with courses in multimedia theory and design.” So I was able to dabble a little in graphic design, 3D animation, music theory, and multimedia computer programming. This is where I was introduced to HTML, CSS, and Flash.

(Note: I did not take any computer science classes, which would have probably pointed me in a different trajectory career-wise. Instead, I was more fascinated with the visual, as opposed to the computational disciplines.)

It can be easy (although probably no easier than any other excuse) to have Imposter Syndrome when your formal education is founded on a multi-disciplinary degree, i.e. Jack of all trades, master of none. However, as some have pointed out…

“Learning isn’t a zero-sum activity.”

The Myth of the Myth of the Unicorn Designer” by Thomas Cole

Code Is Poetry

My first few jobs heavily involved HTML, CSS, and Flash, of course, as well as dabbling in many other languages and systems. However, I quickly gravitated toward WordPress when I was tasked to research alternative content management systems (CMS) for a state college. I started to become familiar with all the concepts that made up a good and bad CMS and was able to research where each private and open source solution lie on the feature vs cost spectrum. I became passionate about the idea of open source software, and WordPress was, and still is, at the forefront.

Today, the WordPress tagline “code is poetry” has become a mantra in my everyday work. So much of what we, as Front-End Developers, write relies on syntax, logic, and structure. Also, good code (as there is plenty of bad code and poetry) requires elegance and simplicity. The meaning with code and poetry can be both on the surface and simultaneously abstract.

Enough About ME!

So why am I giving you my entire bio? Because again, I think it is important to providing context to why I’m fascinated and passionate about composing CSS. In the upcoming posts, I’ll cover some key points along the history of CSS to try and demonstrate where I see CSS evolving. Remember, writing code is a multi-disciplinary venture, and one should never stop learning.

Stay tuned for the next post in this series:

  • Evolution of CSS – Part II: CSS Class Naming Conventions
  • Evolution of CSS – Part III: Overview of Tachyons

The post Evolution of CSS: Becoming a CSS Composer appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2017/02/28/evolution-css-becoming-css-composer/feed/ 0 16183
Our Redesign for The Sports Geek Is a Sure Bet https://webdevstudios.com/2017/02/23/our-redesign-for-the-sports-geek-is-a-sure-bet/ https://webdevstudios.com/2017/02/23/our-redesign-for-the-sports-geek-is-a-sure-bet/#respond Thu, 23 Feb 2017 17:00:58 +0000 http://webdevstudios.com/?p=16265 WebDevStudios is thrilled to announce the launch of the new and improved TheSportsGeek.com! Working with Kevin McClelland, editor of The Sports Geek, was exciting. The challenge that he presented included the requests for a fresh redesign, a logo refresh, and a full-site-rebuild on WordPress. The original website was somewhat outdated, text-heavy, and in need of some visual pizzazz to better Read More Our Redesign for The Sports Geek Is a Sure Bet

The post Our Redesign for The Sports Geek Is a Sure Bet appeared first on WebDevStudios.

]]>

WebDevStudios is thrilled to announce the launch of the new and improved TheSportsGeek.com! Working with Kevin McClelland, editor of The Sports Geek, was exciting. The challenge that he presented included the requests for a fresh redesign, a logo refresh, and a full-site-rebuild on WordPress. The original website was somewhat outdated, text-heavy, and in need of some visual pizzazz to better represent the energy of the client.

Admittedly, I’m not much of a sports guy, but that doesn’t prevent me from designing a great experience while concentrating on the important part: the content. We needed a plan focused on engagement and energy, while retaining the “sports” feel, so as to not alienate current users.

The Process

The first thing you might notice about the old site is that it’s plain; there is a lot of white and red. The header takes up the majority of “the fold,” and the site wasn’t mobile-responsive or mobile-friendly—a design that hearkened back to the early aughts.

“With the world switching to viewing websites on mobile devices, I knew I needed an updated and responsive website, but I was hesitant on getting a new website designed,” explains Kevin. “I thought the process would be confusing and difficult for someone like me who doesn’t have a background in design or coding. Thankfully the WebDevStudios team made the process very easy.”

Kevin came to the design with inspiration drawn from various sports, sports news, and sports betting sites like CBS Sports and Fantasy Pros. Three things they all had in common was that they were content robust, generally monotone, and very modular. So I started there and extrapolated.

“Every step of the way was carefully planned out, and there was an entire team working together to ensure I was happy with all aspects of the website,” Kevin says. “Communication was top notch throughout with weekly calls to keep me updated, and any of my questions or inquiries answered very quickly.”

The Layout

The typical user wasn’t spending much time on the site. Once signed up, users had the opportunity to receive emails with a bulk of fresh content. So, the primary goal was clear that user retention was important, but more so, user acquisition. The majority of the content on the old site was being served up long form without much imagery or differentiation in content. I needed ways to break things up. Enter the card.

A simple but effective way to break up content throughout the site, this layout solution comes in two forms: a “feature” visible on most pages and a general-purpose “card” visible everywhere else. Even the widgets are compartmentalized to keep things separate, but together.

Every part of this site was designed to create a hierarchy and separation between content; thus, influencing user focus, something that was sorely lacking in the old site’s layout.

The Colors

Red plays only a minor role in the new site design. Instead, blue and yellow take center stage, which are colors commonly used in the sports world.

I needed to keep things calm (blue) while bringing focus to elements throughout the site (yellow). Success!

The Header

The old site’s header, video, call to action, and a button is persistent on all pages, which is fine, but taking up that much real estate on a page can be problematic, especially on mobile devices.

Here, I’ve decreased that space requirement, while also giving users the ability to hide this block (not entirely) to clean things up a bit. Though the magic of javascript and cookies, that state (open or closed) is saved for the user; so, they do not constantly have to open or close that header block. Ahh, the internet… What can’t it do?

The Logo

The logo took shape after the site was well into the design phase, which is a fun little design challenge. We were left with a very specific space to fill in the header (longer than it was tall), a limited color palette given the darker blue background, and the need to somewhat refer to the original logo of the old site.

I kept things angular and in the vein of the old logo, but with a little extra flare, grabbing inspiration from existing sports logos, news sites, and for good measure some tech sites (for the geek).

The final logo design came out of our fifth choice, after a few minor tweak and solidifying the type… Voilà!

“When it was time to launch the website, I was eager to see what my followers/fans thought of the new design,” Kevin shares with us. “Within just a few hours of going live, I had numerous emails and tweets from people letting me know how much they loved the re-design; and since then, I’ve still had nothing but positive comments.”

Conclusion

Given several, albeit minor restrictions, we were able to give The Sports Geek a fantastic new look and feel without ignoring its roots. We’ve organized the content and provided a better mobile experience for those catching up on the fly. If at the end of the day, site traffic and new-user acquisition has improved by even a fraction, we’ve done our job. And that’s the power of design.

The post Our Redesign for The Sports Geek Is a Sure Bet appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2017/02/23/our-redesign-for-the-sports-geek-is-a-sure-bet/feed/ 0 16265
How to Migrate a Widget to a Custom Post Type https://webdevstudios.com/2016/12/20/how-to-migrate-a-widget-to-custom-post-type/ https://webdevstudios.com/2016/12/20/how-to-migrate-a-widget-to-custom-post-type/#respond Tue, 20 Dec 2016 15:30:28 +0000 https://webdevstudios.com/?p=13769 Recently, one of my favorite themes, Zerif Lite, was suspended from the WordPress repo. As I was reading about the issues involved, it got me thinking: Just how hard is it to migrate a widget to a Custom Post Type? According to ThemeIsle, this was one of the factors which led to their decision, arguing that “changing this Read More How to Migrate a Widget to a Custom Post Type

The post How to Migrate a Widget to a Custom Post Type appeared first on WebDevStudios.

]]>
Recently, one of my favorite themes, Zerif Lite, was suspended from the WordPress repo. As I was reading about the issues involved, it got me thinking: Just how hard is it to migrate a widget to a Custom Post Type? According to ThemeIsle, this was one of the factors which led to their decision, arguing that “changing this in an existing theme means that whoever’s currently using it will get their site messed up.” Let me be clear: I refuse to take sides here, as there are valid points from both parties, so let’s leave the drama somewhere else. For now, let’s move forward and do something productive. Let’s migrate those old widgets.

Understanding How It Works

First and foremost, to figure this type of thing out, you absolutely must understand (at least very basically) the WordPress Template Hierarchy. Looking at the theme files, there’s the front-page.php file, which we know from looking at the hierarchy; it’s one of the first pages to load when viewing the homepage. We also know by looking at the customizer, for which Zerif was so popular, one of the widget sections is labeled “Our Focus.”

When we look through the front-page.php file, we immediately see the following code, around line 194:

    zerif_before_our_focus_trigger();

        get_template_part( 'sections/our_focus' );
        
    zerif_after_our_focus_trigger();

Luckily for us, the developer made the names quite logical. This snippet uses get_template_part() which is a core WordPress method (if you didn’t already know). This method literally includes a file, if it’s available, relative to the current file location. So, since we’re in the theme root, we now need to open up the sections/our_focus.php file.

Well, we know this section in the customizer uses widgets to create these sections, but we also know widgets are typically called by dynamic_sidebar(). Guess what. That’s just what we have around line 30:

                if ( is_active_sidebar( 'sidebar-ourfocus' ) ) :
                    dynamic_sidebar( 'sidebar-ourfocus' );
                else:

Picking It Apart

To understand how the widget orders are created, we first need to understand how dynamic_sidebar() ensures widgets are in order, and where the data for each widget is stored. You can view this function on trac for more context.

Well, we’re going to have to create a plugin or something similar to get this going, so let’s start with a simple singleton class:

<?php
/**
 * Plugin Name:     Widget to CPT
 * Plugin URI:      http://plugish.com
 * Description:     A simple how-to for migrating widgets to a CPT
 * Author:          JayWood
 * Author URI:      http://plugish.com
 * Version:         0.1.0
 */

class JW_Widget_to_CPT {

    /**
     * Instance of JW_Widget_to_CPT
     * @var JW_Widget_to_CPT
     */
    public static $instance = null;

    public static function init() {
        if ( null == self::$instance ) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }

    public function hooks() {
        // All hooks here.
    }
}

function jw_widget_to_cpt() {
    return JW_Widget_to_CPT::init();
}
add_action( 'plugins_loaded', array( jw_widget_to_cpt(), 'hooks' ) );

Looking through the dynamic sidebar method, we first see two globals: $wp-registered_sidebars and $wp_registered_widgets respectively.

So, how do we figure out what they show?

I wrote a post awhile back about debugging WordPress with tips and snippets. You may want to check that out.

The example below shows you how to do this:

    public function hooks() {
        // All hooks here.
        add_action( 'init', array( $this, 'kill_it' ) );
    }

    public function kill_it() {
        global $wp_registered_sidebars, $wp_registered_widgets;

        error_log( print_r( compact( 'wp_registered_sidebars', 'wp_registered_widgets' ), 1 ) );
    }

This will produce two arrays in your debug log. Sidebar: You are using logging, aren’t you? If not, that’s covered in my aforementioned article, as well. Go read it!

The first array is all sidebars that are registered it looks something like so:

[16-Sep-2016 02:30:47 UTC] Array
(
    [wp_registered_sidebars] => Array
        (
...
            [sidebar-ourfocus] => Array
                (
                    [name] => Our focus section widgets
                    [id] => sidebar-ourfocus
                    [description] =>
                    [class] =>
                    [before_widget] =>
                    [after_widget] =>
                    [before_title] => <h2 class="widgettitle">
                    [after_title] => </h2>

                )
...

The second array is a list of registered widgets, which looks like so:

[wp_registered_widgets] => Array
        (
...
            [ctup-ads-widget-1] => Array
                (
                    [name] => Zerif - Our focus widget
                    [id] => ctup-ads-widget-1
                    [callback] => Array
                        (
                            [0] => zerif_ourfocus Object
                                (
                                    [id_base] => ctup-ads-widget
                                    [name] => Zerif - Our focus widget
                                    [widget_options] => Array
                                        (
                                            [classname] => widget_ctup-ads-widget
                                            [customize_selective_refresh] =>
                                        )

                                    [control_options] => Array
                                        (
                                            [id_base] => ctup-ads-widget
                                        )

                                    [number] => 4
                                    [id] => ctup-ads-widget-4
                                    [updated] =>
                                    [option_name] => widget_ctup-ads-widget
                                )

                            [1] => display_callback
                        )

                    [params] => Array
                        (
                            [0] => Array
                                (
                                    [number] => 1
                                )

                        )

                    [classname] => widget_ctup-ads-widget
                    [customize_selective_refresh] =>
                )
...

Now, just one more thing. We need to figure out exactly how these widgets are ordered within each sidebar. Well, luckily there’s a function for that; it’s called wp_get_sidebars_widgets(), and the output from this method on a default install is like so:

[16-Sep-2016 02:59:09 UTC] Array
(
    [wp_inactive_widgets] => Array
        (
        )

    [sidebar-1] => Array
        (
            [0] => search-2
            [1] => recent-posts-2
            [2] => recent-comments-2
            [3] => archives-2
            [4] => categories-2
            [5] => meta-2
        )

    [sidebar-aboutus] =>
    [zerif-sidebar-footer] =>
    [zerif-sidebar-footer-2] =>
    [zerif-sidebar-footer-3] =>
    [sidebar-ourfocus] => Array
        (
            [0] => ctup-ads-widget-1
            [1] => ctup-ads-widget-2
            [2] => ctup-ads-widget-3
            [3] => ctup-ads-widget-4
        )

    [sidebar-testimonials] => Array
        (
            [0] => zerif_testim-widget-1
            [1] => zerif_testim-widget-2
            [2] => zerif_testim-widget-3
        )

    [sidebar-ourteam] => Array
        (
            [0] => zerif_team-widget-1
            [1] => zerif_team-widget-2
            [2] => zerif_team-widget-3
            [3] => zerif_team-widget-4
        )

)

Okay, So Now What?

Now comes the fun!

We now know what all the data arrays look like, so we now have some context as to what dynamic_sidebar() is actually doing. Time to dive in!

Figuring out the type of data you’re expecting is probably the most important aspect to reverse engineering how something works. Keep in mind, we’re looking for all widgets in the sidebar-ourfocus dynamic sidebar location.

Digging deeper into the dynamic_sidebar() method, we can skip over a few checks and right down to the foreach loop it is running. We now see that it’s comparing registered widgets to the widget order array we pulled; if for some reason the widget is no longer registered, it just will not display:

        if ( !isset($wp_registered_widgets[$id]) ) continue;

By this point, we know the ID of the widgets under the “Our Focus” section are ‘ctup-ads-widget-X,’ where X is like an index (not to be confused with array keys). So, now we have the IDs, and know that the dynamic sidebar now renders the output of the widget. We have to dive into widgets and see just exactly how the widget data is saved.

Looking around the Zerif Lite theme, you’ll find the “Our Focus” was located in the main functions file under the class name of zerif_ourfocus, which extends the main WP_Widget class. Simple enough, but take note of the update method and its instance keys:

  • Text ( text )
  • Title ( title )
  • Link ( link )
  • Image ( image_uri )
  • Custom Media ID ( custom_media_id )

Now, at this point, I took a shortcut.

WordPress tutorials, WordPress how-to, WordPress education, learn WordPress, Zerif Lite theme, #WPdrama, WordPress drama, Zerif LIte banned, WordPress banned Zerif Lite, ThemeIsle banned Zerif Lite, migrate a widget to a custom post type, how to work with custom post types

From my history with WordPress, I know that widget options are stored in the options table, so again, putting two and two together, I figured I could run a query on the database to find exactly what the option key was. Knowing that widget names are used in the option names, here’s my query:

select * from wp_options where option_name like "%ctup%";

Which gave me this amazingly jumbled mess…but still, it’s serialized data, and still useful if you utilize a tool like Unserialize.com. If you didn’t know that existed, it’s super handy (and you can thank Brad Parbs for that little gem!).

Where did I get that ‘ctup’ in my SQL query? That’s part of the widget ID which you can see in the functions file. Granted, if there were more widgets which used a similar string, I’d have a harder time finding the options, but that’s not the case here.

Finally a Resolution…

So, what do we know so far?

  • We know how the IDs are associated across multiple arrays.
  • We also now know what our options look like for each individual widget.

“What’s left?” you ask. Well, the actual migration, of course. We need to get the data from point A to point B.

We first need to get the the list of widgets in our sidebar. We know the sidebar is labeled sidebar-ourfocus, so let’s grab that from the widgets array we previously created. We’re also going to go ahead and grab the options for our widget ID.

We also want to make sure we set up an option to check, to avoid migrating this stuff more than once. So, let’s create a simple migrate method, but don’t hook into it just yet.

    public function migrate() {

        if ( get_option( 'zl_has_imported', false ) ) {
            return;
        }

        $widgets        = wp_get_sidebars_widgets();
        $widget_set     = $widgets['sidebar-ourfocus'];
        $widget_options = get_option( 'widget_ctup-ads-widget' );

    }

Now possibly the simplest part of all, the loop to migrate the widgets to the CPT. First off here, we need to loop over all widgets in the sidebar-ourfocus array we just grabbed. Let’s look at the full loop, and I’ll explain:

        foreach ( $widget_set as $index => $widget ) {

            // Grab the ID of the widget
            $widget_array = explode( '-', $widget );

            /**
             * Now we grab the number ( key ) from the widget
             * So ctup-ads-widget-1 for example will give us just the number 1
             */
            $widget_key = end( $widget_array );

            /**
             * The above grabbed key is associated with the array keys in the
             * widget options, so we use that one here.
             */
            $widget_data = $widget_options[ $widget_key ];

            /**
             * Now that we have all our widget data
             * we build up the insertion arguments
             * for wp_insert_post()
             */
            $insert_args = array(
                'post_type'    => 'focus',
                'post_status'  => 'publish',
                'menu_order'   => $index,
                'post_title'   => $widget_data['title'],
                'post_content' => $widget_data['text'],
                'meta_input'   => array(
                    'link'      => $widget_data['link'],
                    'image_uri' => $widget_data['image_uri']
                ),
            );

            wp_insert_post( $insert_args );
        }

        update_option( 'zl_has_imported', true );

At first, we do a little trickery to grab the widget IDs, which correspond to the keys in the widget options data we grabbed earlier. There’s a nifty function in PHP to grab the end of an array, who would’ve guess it was called end()? This little gem sets the internal pointer of an array to the last element in the array, and returns it; a bit easier than other ways of grabbing last array items, I’d say.

Finally, the insertion arguments are pretty straightforward, dare I say. We’re storing the link and image_uri fields as custom post data. But notice the menu_order argument. This is because we want to retain the order in which the widgets were originally displayed.

Finally, after the loop, our posts should be imported; let’s update that option with true, so we don’t accidentally import the widgets again.

But, Wait! That’s an Invalid Post Type.

Yup, you are correct. Registering post types is the easiest thing to do, so I admittedly just copy and pasted from the codex, and changed a few things. Since we’re going to be here, we can also go ahead and hook into our migrate method.

Here’s your snippet for that, including the hooks method, which you obviously need:

    public function hooks() {
        // All hooks here.
        add_action( 'init', array( $this, 'migrate' ) );
        add_action( 'init', array( $this, 'register_post_type' ) );
    }

    public function register_post_type() {
        $labels = array(
            'name'               => _x( 'Focus', 'post type general name', 'your-plugin-textdomain' ),
            'singular_name'      => _x( 'Focus', 'post type singular name', 'your-plugin-textdomain' ),
            'menu_name'          => _x( 'Focus', 'admin menu', 'your-plugin-textdomain' ),
            'name_admin_bar'     => _x( 'Focus', 'add new on admin bar', 'your-plugin-textdomain' ),
            'add_new'            => _x( 'Add New', 'book', 'your-plugin-textdomain' ),
            'add_new_item'       => __( 'Add New Focus', 'your-plugin-textdomain' ),
            'new_item'           => __( 'New Focus', 'your-plugin-textdomain' ),
            'edit_item'          => __( 'Edit Focus', 'your-plugin-textdomain' ),
            'view_item'          => __( 'View Focus', 'your-plugin-textdomain' ),
            'all_items'          => __( 'All Focus', 'your-plugin-textdomain' ),
            'search_items'       => __( 'Search Focus', 'your-plugin-textdomain' ),
            'parent_item_colon'  => __( 'Parent Focus:', 'your-plugin-textdomain' ),
            'not_found'          => __( 'No focus found.', 'your-plugin-textdomain' ),
            'not_found_in_trash' => __( 'No focus found in Trash.', 'your-plugin-textdomain' )
        );

        $args = array(
            'labels'             => $labels,
            'description'        => __( 'Description.', 'your-plugin-textdomain' ),
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'query_var'          => true,
            'rewrite'            => array( 'slug' => 'focus' ),
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => true,
            'menu_position'      => null,
            'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments', 'page-attributes' )
        );

        register_post_type( 'focus', $args );
    }

Conclusion

Migrating widgets to a CPT (while a really weird request), can be done. This article was written as a proof of concept on how it can be handled. Now, admittedly, for migrations I use WP-CLI, and hardly ever do migrations on init. In fact, if you don’t know about WP-CLI already, you should really check it out. Overall, this was a quick and dirty example of how to handle a widget to CPT migration, purely because I like to solve puzzles, and I like showing you how to do the same.

Overall, I hope this was helpful and insightful into the beast that is WordPress. As a developer, there is always more than one way to do something, so take this article with a grain of salt and modify as you see fit.

Get the full source code for this plugin.

The post How to Migrate a Widget to a Custom Post Type appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/12/20/how-to-migrate-a-widget-to-custom-post-type/feed/ 0 13769
How to Download FTP Files in WordPress https://webdevstudios.com/2016/10/19/how-to-download-ftp-files-in-wordpress/ https://webdevstudios.com/2016/10/19/how-to-download-ftp-files-in-wordpress/#comments Wed, 19 Oct 2016 14:44:05 +0000 https://webdevstudios.com/?p=13311 Recently, I was given the opportunity to work on a really cool importing project that involved us pulling data from an FTP server and importing that into WordPress. It led me to look deeper into the Filesystem API; WordPress does this already, and I wanted to learn more. There’s a couple of hurdles you have to get Read More How to Download FTP Files in WordPress

The post How to Download FTP Files in WordPress appeared first on WebDevStudios.

]]>
Recently, I was given the opportunity to work on a really cool importing project that involved us pulling data from an FTP server and importing that into WordPress. It led me to look deeper into the Filesystem API; WordPress does this already, and I wanted to learn more.
There’s a couple of hurdles you have to get over if you’re not familiar with file manipulations, so let’s jump into it, and hopefully I can show you some neat tricks!

How does it work?

As of WordPress 4.5.2, the WP_Filesystem_FTPext class is located at wp-admin/includes/class-wp-filesystem-ftpext.php. The FTP class allows the developer to connect to an FTP server and abstracts out some of the common FTP commands you may not be aware of.

With this class, you can do the following directly on the server you’re connected to:

  • Get the contents of any file
  • Get a contents array ( gets the contents of the file and separates each line into it’s own array key )
  • Put the contents INTO a file
  • List the directory you’re currently in
  • Copy files to/from the FTP server
  • More…

There are also some file attribute-related methods as well that allow you to:

  • chmod a file
  • Get the owner of a file
  • Get the chmod of a file
  • Get the group of a file
  • Determine the size of the remote file
  • Determine if you can read/write to that file
  • More…

Simply put, this is a really robust class for abstracting typical FTP operations.

Okay, so what to do with it?

As you know, WordPress already utilizes this FTP class to do updates, install plugins, etc. However, in this special case, we needed to get the file from a remote server, insert it as an attachment, and later import that file.

The first immediate goal is to connect to FTP so we can get what we need. For the purposes of this article, we’re going to just use a simple singleton class. So here you go!

<?php

class JW_Tests {

    /**
     * Instance of JW_Tests
     * @var JW_Tests
     */
    public static $instance = null;

    public static function init() {
        if ( null == self::$instance ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function hooks() {
        // All hooks here.
        add_action( 'init', array( $this, 'main' ) );
    }

    public function main() {
        // Fancy stuff here
    }
}

function jw_tests() {
    return JW_Tests::init();
}
add_action( 'plugins_loaded', array( jw_tests(), 'hooks' ) );

Now that we have the basic scaffolding setup, we need to jump into actually making the connection. We can do that in the main method of our new singleton.

I cannot stress this enough: You absolutely should NOT store the FTP credentials to this or ANY connection, either in the code, or even in the database. You have been warned!

We don’t want to fire this on EVERY init of WordPress, so adding a $_GET flag will allow us to execute a download for testing purposes.

        if ( ! isset( $_GET['download'] ) ) {
            return;
        }

Next, we have to make sure we have access to the classes we need, so we first check if these classes are available, and if not, we load them in. We’re going to need wp_tempnam() specifically for this occasion. Since we obviously need the FTPext class, it extends Filesystem_Base, and we can’t have one without the other.

        // First load the necessary classes
        if ( ! function_exists( 'wp_tempnam' ) ) {
            require_once( ABSPATH . 'wp-admin/includes/file.php' );
        }

        if ( ! class_exists( 'WP_Filesystem_Base' ) ) {
            require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' );
        }

        if ( ! class_exists( 'WP_Filesystem_FTPext' ) ) {
            require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-ftpext.php' );
        }

Now, in the past I had issues with the FS_CONNECT_TIMEOUT constant not being defined, so I went ahead and included that here (it’s required by the FTP class). I really didn’t see any sense in loading another entire class into memory just for one constant.

        // Typically this is not defined, so we set it up just in case
        if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) {
            define( 'FS_CONNECT_TIMEOUT', 30 );
        }

Once all that fancy stuff is done, and all checks have passed we setup our connection arguments, we can finally connect() to the ftp server (or bail if for some reason the connection failed).

                /**
         * You DO NOT want to hard-code this, these values are here specifically for
         * testing purposes.
         */
        $connection_arguments = array(
            'port' => 21,
            'hostname' => '127.0.0.1',
            'username' => '',
            'password' => '',
        );

        $connection = new WP_Filesystem_FTPext( $connection_arguments );
        $connected = $connection->connect();
        if ( ! $connected ) {
            return false;
        }

Great, we’re connected. Now what?

If you’re this far, you probably know of a file you want to download. Let’s download that file and import it as an attachment! Might want to grab a drink and a snack, because there’s going to be a decent amount of code ahead.

First things first, we need to make sure the file is indeed a file. To do this, we make use of the is_file() method located in our connection we made previously, and if not, we bail:

        $remote_file = "x.csv";
        // Yep, you can use paths as well.
        // $remote_file = "some/remote-file.txt";
        if ( ! $connection->is_file( $remote_file ) ) {
            return;
        }

Great, we have a file. Now you need to use the get_contents() method to grab the remote file’s contents into PHP memory, so we can push it elsewhere later.

        // Get the contents of the file into memory.
        $remote_contents = $connection->get_contents( $remote_file );
        if ( empty( $remote_contents ) ) {
            return;
        }

Now before I go further, let me rant a little.

For some context, let’s look at the get_contents()method of the WP_Filesystem_FTPextclass.

The method does the following in order:

  1. Uses wp_tempnam() to create a temporary file
  2. Opens the file with fopen, if not, it will bail
  3. Runs ftp_fget (PHP’s native FTP method) to stream the remote file into the new temporary file, bail if it cannot
  4. Rewinds the temp file pointer back to zero (kinda like a VHS tape…yeah, I said it!)
  5. Now, it reads the entire temp file into memory and returns it, kinda like file_get_contents()

So, what’s my gripe? We don’t have access to this temp file name, at all. Sure, we could re-create the entire method of streaming the file just as the method currently does, but why re-invent the wheel if it’s already spinning? I smell a patch request coming! (View the WP_Filesystem_FTPext::get_contents() source to see what I mean.) You’ll see why this is a problem below.

Now that we have the remote contents in memory, we need to create our OWN file… despite the fact it was already created by core in the above step. We just can’t access it, which sucks. You’ll notice in the below snippet that we’re checking if the file is writable; this is because we’re also using file_put_contents() to push our data in memory into the file. If, for some reason, pushing the content fails, or the temp file isn’t writable, we need to bail and cleanup after ourselves with unlink() –be kind to the server.

        // Create a temporary file to store our data.
        $temp_file = wp_tempnam( $remote_file );
        if ( ! is_writable( $temp_file ) || false === file_put_contents( $temp_file, $remote_contents ) ) {
            unlink( $temp_file );
            return;
        }

For those who may not know, unlink() will delete a file in PHP given the absolute path. Always make sure you clean up after yourself!

We need a bit more data for the side-load functionality we’re getting ready to dive into. The codex for wp_handle_sideload() DOES actually hard-code the mime type, but in this example, I’m using wp_check_filetype() so we can check the mime type against any extra registered types. If for some reason the type isn’t found for our file extension (in this case CSV), we gracefully exit, and again, clean up after ourselves.

        // Optimally you want to check the filetype against a WordPress method, or you can hard-code it.
        $mime_data = wp_check_filetype( $remote_file );
        if ( ! isset( $mime_data['type'] ) ) {
            // WE just don't have a type registered for this attachment
            unlink( $temp_file ); // Cleanup
            return;
        }

Woohoo, we’re almost there! Next up is the easy part: The side-load into the uploads directory.

These file arrays were copied from the sideload example in the codex; once again, no need to re-invent the wheel. That said, there are exceptions in the code. For instance, $temp_file, which we created earlier, already exists, and we also grabbed the mime-type from core, instead of hard-coding it, and finally we can also use the pre-made file string which we defined above, as the name of the side-loaded file.

        /**
         * The following arrays are pretty much a copy/paste from the Codex, no need
         * to re-invent the wheel.
         * @link https://codex.wordpress.org/Function_Reference/wp_handle_sideload#Examples
         */
        $file_array = array(
            'name'     => basename( $remote_file ),
            'type'     => $mime_data['type'],
            'tmp_name' => $temp_file,
            'error'    => 0,
            'size'     => filesize( $temp_file ),
        );

        $overrides = array(
            'test_form'   => false,
            'test_size'   => true,
            'test_upload' => true,
        );

        // Side loads the content into the wp-content/uploads directory.
        $sideloaded = wp_handle_sideload( $file_array, $overrides );
        if ( ! empty( $sideloaded['error'] ) ) {
            return;
        }

If no errors happened, you should get an array back like this from the wp_handle_sideload() method:

array(
    'file' => '/var/www/example.com/wp-content/uploads/2016/08/x.csv'
    'url' => 'http://example.com/wp-content/uploads/2016/08/x.csv'
    'type' => 'text/csv'
)

Finally, we’re at the end of the line. Up to this point we’ve successfully downloaded a file from an FTP server, stored it as a temporary file, and utilized WordPress core methods to side-load it. But wait, there’s more….what?!?! I know that’s what you’re thinking! Bear with me here.

WordPress does not automatically ‘know’ files are in the uploads directory. This is what the database is for–so in order for you to use your newly downloaded file as an attachment, we need to create an attachment. It can be done in just a few lines of code:

        // Will return a 0 if for some reason insertion fails.
        $attachment_id = wp_insert_attachment( array(
            'guid'           => $sideloaded['url'], // wp_handle_sideload() will have a URL array key which is the absolute URL including HTTP
            'post_mime_type' => $sideloaded['type'], // wp_handle_sideload() will have a TYPE array key, so we use this in case it was filtered somewhere
            'post_title'     => preg_replace( '/\.[^.]+$/', '', basename( $remote_file ) ), // Again copy/paste from codex
            'post_content'   => '',
            'post_status'    => 'inherit',
        ), $sideloaded['file'] ); // wp_handle_sideload() will have a file array key, so we use this in case it was filtered

        // SUCCESSSSSSSSS!!!!!!!!!!!

This is where we make use of the array data we got back from wp_handle_sideload(). After all, we want to use what WordPress gives us–if for some reason down the road something changes, or is filterable.

That’s all, folks!

So we’ve successfully downloaded from a remote FTP server and created an attachment for the downloaded file. This article only skims the surface of what’s possible with FTP downloads (especially in WordPress). You can do SO much more–download remote product CSV’s for import, FTP image downloads…just think about it!

Overall, I hope this at least opened your eyes to some possibilities and give you that spark of interest to create something awesome. At the very least I’d like to hear from you: What would you do differently? Did you like this article? Let me know! Let’s grow as developers, together!

Full source

You didn’t think I forgot to give you the source did you? Enjoy.

The post How to Download FTP Files in WordPress appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/10/19/how-to-download-ftp-files-in-wordpress/feed/ 1 13311
Free Webinar: Turn Your WordPress Site Into a Mobile App https://webdevstudios.com/2016/09/15/free-webinar-turning-your-wordpress-site-into-a-mobile-app/ https://webdevstudios.com/2016/09/15/free-webinar-turning-your-wordpress-site-into-a-mobile-app/#comments Thu, 15 Sep 2016 12:00:25 +0000 https://webdevstudios.com/?p=13759 Wow, where does the time go? It has already been two months since our previous webinar. As you may recall, our CEO, Brad Williams, shared his experience working with WordPress in the enterprise space. The hour-long webinar was filled with great information from Brad and wrapped up with a number of excellent questions from the Read More Free Webinar: Turn Your WordPress Site Into a Mobile App

The post Free Webinar: Turn Your WordPress Site Into a Mobile App appeared first on WebDevStudios.

]]>
Wow, where does the time go? It has already been two months since our previous webinar. As you may recall, our CEO, Brad Williams, shared his experience working with WordPress in the enterprise space. The hour-long webinar was filled with great information from Brad and wrapped up with a number of excellent questions from the live audience. It felt just like being at a WordPress conference, except the entire audience was in their PJs (or so we assume! I know I was.).

We had such a great time doing the first webinar, we wanted to turn right around and do it all over again. So mark your calendar: On October 6th, 2016, we’re hosting our next webinar: Your WordPress Site Into a Mobile App, hosted by me, John Hawkins.

A couple of months back, after being head down in AppPresser, I decided to learn firsthand what it takes to turn a WordPress site into a mobile app. I wanted to learn the entire process, from start to finish (using both AppPresser and otherwise), and so I did!

If you’ve been wondering how to do it, don’t miss this opportunity to pick my brain on the process. I’ll be sharing what worked, what didn’t, the shortcuts I discovered, and the pitfalls I came across.

Here are the details:

Turn Your WordPress Site Into A Mobile App

Date: October 6th, 2016
Time: 1pm – 2pm ET

Signup Here

First name:
Last name:
Email:

The webinar is totally free; all you have to do is sign up! For those of you who can’t make it day of, we will be providing a recording of the talk as well–just make sure you put your info in beforehand so we can send it out to you afterward.

We hope to see you there (and psst…if there are any questions that are pressing now and that you’d like to see addressed, drop them in the comments)!

 

The post Free Webinar: Turn Your WordPress Site Into a Mobile App appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/09/15/free-webinar-turning-your-wordpress-site-into-a-mobile-app/feed/ 4 13759
What WDS Did for Five for the Future: July 2016 https://webdevstudios.com/2016/08/04/five-for-the-future-july-2016/ https://webdevstudios.com/2016/08/04/five-for-the-future-july-2016/#respond Thu, 04 Aug 2016 17:42:48 +0000 https://webdevstudios.com/?p=13565 Last week, Brad popped in to tell you about the changes we’ve made to how we do Five for the Future. We had our first full day of #5FTF, rather than two hours per employee every Monday, last week, and I’m here to forward details from the team about what we worked on! We had five teams: Team Read More What WDS Did for Five for the Future: July 2016

The post What WDS Did for Five for the Future: July 2016 appeared first on WebDevStudios.

]]>
Last week, Brad popped in to tell you about the changes we’ve made to how we do Five for the Future. We had our first full day of #5FTF, rather than two hours per employee every Monday, last week, and I’m here to forward details from the team about what we worked on!

We had five teams:

  • Team BBM, working on the migration framework
  • Team Bangarang, working on CMB2
  • Team Pirate Party Parrots, working on wd_s
  • Team #MakeDonaldDrumfAgain, working on the Mega Menu Plugin
  • A Scream Team, which was everyone working on solo projects

WebDevStudios, WordPress community, open source community, Five to the Future, WordPress developers, WordPress contributors

Team BBM, working on the migration framework

We’re working on a free version we can release to the repository!

John researched and reviewed documentation, created graphics for WordPress.org, and started work on readme.txt file for .org release.

Jeremy worked on the unit tests for the migrations framework in preparation for more sweeping changes to the plugin. The existing unit tests weren’t passing properly, so he spent the day working through all of the failures to get the to pass so that he could start off in a good place. In the process, he did some code cleanup, and corrected numerous issues with the tests themselves.

Team Bangarang, working on CMB2

Benjamin added support for clearing the search field placeholder text in wd_s when the field is clicked on and then adding the placeholder text back in when focus of the field is lost. He also updated the CMB2 documentation for the colorpicker field to include the changes made that allow configuring the Iris default options.

Team Pirate Party Parrots, working on wd_s

The team had a call in the morning where they added issues (new requests/features/bugs) to wd_s on Github, then assigned those issues out to each member of the team. After that, they went for it!

Greg managed all merges and wd_s repository on Github as a whole, and acted as the “gatekeeper.” He also helped squash a bunch of bugs. Corey did several CSS updates, and some JS updates to bring wd_s forward with responsive modals, updated to proper button styling/functionality, and did research into the possible SEO impact of including title tags in SVG output. Will brought up a number of items that became issues for the team to work on this morning during the call, and contributed to helping work those out.

Allison strengthened mobile styles for default nested comments, added new template tags for additional features re: read more / excerpts, brought wd_s up to snuff re: comments, and worked on finding the best way to crunch that IE bug. Jo squashed some wd_s issues, including making default responsive table styles and worked out an IE button bug.

Team #MakeDonaldDrumfAgain, working on the Mega Menu Plugin

Zach added an options page that allows controlling the menu depth from the UI, instead of needing to be set in code. This page is set up in a way to be easily extensible for additional options. Zach said this was a great way to familiarize with more of WP’s menu system. Chris did a “buttload of commits” and led the team.

A Scream Team, solo projects

Parbs did some work for the next version of vv, then tested new features. He also is working on developing Docker version of vv.

Matt tested ES/EP performance issues, aggregation research, tested updated mappings for ngrams/synonym filters and must/should match boolean queries, as well as debugged pagination with ES and localhost issues with ES VM. (Pssst…he wrote a tutorial on integrating ElasticPress into WordPress awhile back, too!)

Carrie worked on Alcatraz theme. She added a new page option to the page metaboxes that allow the user to override the site’s default layout option (which is usually boxed, boxed content, or full-width). This is really helpful when working on a site that you typically want to have say, a boxed layout, but maybe on a landing page want to have a full-page layout (where rows and images stretch the entire width of the screen). She also worked on making improvements to the navigation Sass.

Jay built in multi-server support to my JSON API for WooCommerce. The only thing left is the Java portion, which isn’t WordPress related, and will be done on his own time. Significant refactoring was done on how commands were saved and displayed to the JSON API as well as new drop-down elements to allow users to add/remove servers and select said servers for commands to be executed on. This was one of THE MOST requested items on this plugin, and is now complete, thanks to #5ftf time!

To sum up what this plugin does, it bridges the gap between WooCommerce, and Minecraft. Jay’s creation of the JSON endpoint for WooCommerce, the Java plugin (installed on the Minecraft server) reads data FROM WordPress and executes commands based on that data. Having a JSON endpoint on WordPress side of things allows pretty much unlimited possibilities, but does require the accompanying Java code to translate it into game readable information.

Eric worked on creating a WP theme with React/React Router/Redux and the WP-API. He did a lot of code refactoring, integrated Material UI, and a bit of styling to make the site at least look presentable.

Damon caught up on WP Contributions plugin as haven’t been touched in awhile. He closed a few issues and added shortcode feature.

Aubrey released version 1.3.9 of Easy Photo Album, which updated a two year old plugin to work again in the latest WP 4.5.3!

Shannon worked on refactoring an old plugin to get it ready for re-release, making it play nicely with the Settings API and TinyMCE.

Ben worked on refactoring and bug hunting on the WordPress Statsd plugin.


We were all over Twitter, sharing our WDS selfies and more! We’re stoked about this new way we get stuff done–the consensus was that we got to rally more enthusiasm and focus, which made us way more productive in how we’re giving back to WordPress.

Thanks for joining us on the ride! We’ll see you next month, folks!

The post What WDS Did for Five for the Future: July 2016 appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/08/04/five-for-the-future-july-2016/feed/ 0 13565
A Change in Five for the Future at WebDevStudios https://webdevstudios.com/2016/07/27/a-change-in-five-for-the-future-at-webdevstudios/ https://webdevstudios.com/2016/07/27/a-change-in-five-for-the-future-at-webdevstudios/#respond Wed, 27 Jul 2016 16:20:37 +0000 https://webdevstudios.com/?p=13506 For almost two years now, WebDevStudios has been participating in the Five for the Future movement. During that time we have contributed thousands of hours to the WordPress community across various projects. Contributing to the WordPress community, and open source in general, is at the core of what we do at WDS. We have always Read More A Change in Five for the Future at WebDevStudios

The post A Change in Five for the Future at WebDevStudios appeared first on WebDevStudios.

]]>
For almost two years now, WebDevStudios has been participating in the Five for the Future movement.

During that time we have contributed thousands of hours to the WordPress community across various projects. Contributing to the WordPress community, and open source in general, is at the core of what we do at WDS. We have always been thankful to WordPress for allowing us to do what we love day in and day out, and contributing 5% of our company time back to the project is our way of officially giving back to this awesome community.

When we started participating in Five for the Future, we scheduled two hours per week for each employee. Our standard contribution time was every Monday, from 9 – 11 AM. Overall, this schedule worked great and allowed our team to start the week in a very positive and fun way. We still felt like we could do better, so we have decided to make a change.

Starting this Friday, WebDevStudios will dedicate one full day–the last Friday–of each month as our Five for the Future day!

#5FTF, Five for the Future, WordPress contributing, contributing to WordPress, WordPress community

Dedicating an entire day to #5FTF, instead of just two hours per week, will allow our team to dive deeper into their contribution. We are also forming teams around specific projects, including CMB2, wd_s, plugin generator, and our mega menu plugin. Of course, our team still has the option of contributing to any part of the WordPress project they choose, as well.

celebrate

I’m really excited for our first #5ftf day at WebDevStudios this Friday! Make sure you keep tabs on our team’s Twitter accounts (and our main account, @webdevstudios) for updates throughout the day.

The post A Change in Five for the Future at WebDevStudios appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/07/27/a-change-in-five-for-the-future-at-webdevstudios/feed/ 0 13506
Why Enterprise Companies Should Use WordPress: Webinar Debriefing https://webdevstudios.com/2016/07/21/why-companies-use-wordpress-webinar-debriefing/ https://webdevstudios.com/2016/07/21/why-companies-use-wordpress-webinar-debriefing/#respond Thu, 21 Jul 2016 17:32:53 +0000 https://webdevstudios.com/?p=13488 Since we’ve been posting about it constantly for the last month, you probably already know that we hosted our first webinar this past Monday. Brad talked about why enterprise companies should use WordPress, and detailed some of the basic misconceptions business owners have about WordPress. In case you missed it, we have a treat for you! Read More Why Enterprise Companies Should Use WordPress: Webinar Debriefing

The post Why Enterprise Companies Should Use WordPress: Webinar Debriefing appeared first on WebDevStudios.

]]>
Since we’ve been posting about it constantly for the last month, you probably already know that we hosted our first webinar this past Monday. Brad talked about why enterprise companies should use WordPress, and detailed some of the basic misconceptions business owners have about WordPress.

In case you missed it, we have a treat for you!

 

You can watch it right here and now:

A few highlights:

“A lot of the reasons that WordPress has become so popular–there aren’t really restrictions. You can use it for whatever you want.”

“A lot of people think WordPress can’t handle a large load of traffic. It’s not true. WP can handle millions and millions of hits. Really, the only limitations of what WordPress can handle depends on what your hosting can handle.”

“WordPress is a platform that people can understand how to use. Your team will want to use the tools you provide them and manage the content and manage the media because WordPress makes it easy…If your platform is hard to use, your team won’t want to use it.”


Thank you to all that attended and to those who sent us questions!

We’ll be hosting another webinar, featuring our Director of Products and Pluginize go-to, John, so keep an eye out for more details on that in a few weeks!

Have more questions about the above webinar or topics you’d like to see addressed during one of these? Drop ’em in the comments and let us know! We want your feedback!

The post Why Enterprise Companies Should Use WordPress: Webinar Debriefing appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/07/21/why-companies-use-wordpress-webinar-debriefing/feed/ 0 13488