Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
poxrud committed Aug 21, 2023
1 parent 66f837d commit 22f32bf
Show file tree
Hide file tree
Showing 17 changed files with 421 additions and 62 deletions.
2 changes: 1 addition & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ baseurl: ""
title: "Web Development Blog by Phil Oxrud"
email: [email protected]
description: >-
A blog from Phil Oxrud about advanced concepts in web development, programming, and the Cloud.
A blog by Phil Oxrud about advanced concepts in web development, programming, and the Cloud.
twitter: poxrud
github: poxrud
Expand Down
2 changes: 1 addition & 1 deletion _includes/icon-github.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<a href="https://github.com/{{ site.github }}">
<svg class="h-6 w-5">
<svg class="h-6 w-5 hover:fill-blue">
<use xlink:href="#github" />
</svg>
</a>
2 changes: 1 addition & 1 deletion _includes/icon-twitter.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<a href="https://twitter.com/{{ site.twitter }}">
<svg class="h-6 w-5">
<svg class="h-6 w-5 hover:fill-blue">
<use xlink:href="#twitter" />
</svg>
</a>
13 changes: 0 additions & 13 deletions _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,6 @@ <h1 class="hero__headline text-6xl text-cool-orange">Web Development and the Clo
</nav>
</header>


<section>
<div class="flex">
<img class="rounded-full object-fill h-16" src="/assets/philoxrud.jpg" class="avatar" />
{{ content }}
</div>
<div class="flex gap-2">
{% include icon-twitter.html %}
{% include icon-github.html %}
</div>
</section>


{{ content }}

<footer class="text-center mt-8">
Expand Down
30 changes: 14 additions & 16 deletions _layouts/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,34 @@
---

<div class="main">
<article class="mt-6 text-base text-zinc-600 dark:text-zinc-400">
{{ content }}
</article>
<section>
<div class="flex gap-8">
<img class="rounded-full object-fill h-16" src="/assets/philoxrud.jpg" class="avatar" />
{{ site.description }}
</div>
<div class="flex gap-2 py-3 justify-end">
{% include icon-twitter.html %} {% include icon-github.html %}
</div>
</section>

<div
class="my-8 mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none lg:grid-cols-2"
>
<div class="mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none lg:grid-cols-2">
<section>
<h2
class="text-left text-2xl font-semibold tracking-tight text-zinc-800 dark:text-zinc-100"
>
<h2 class="text-left text-2xl font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
My Articles and Tutorials
</h2>
<ul>
{% for post in site.posts %}
<li class="pt-4">
{% assign date_format = site.minima.date_format | default: "%b %-d,
%Y" %}
<time class="text-sm text-zinc-400 dark:text-zinc-500"
>{{ post.date | date: date_format }}</time
>
<time class="text-sm text-zinc-400 dark:text-zinc-500">{{ post.date | date: date_format }}</time>
<p class="text-base tracking-tight text-zinc-800 dark:text-zinc-100">
<a href="{{ post.url | relative_url }}"
>{{ post.title | escape }}</a
>
<a href="{{ post.url | relative_url }}" class="text-blue-500">{{ post.title | escape }}</a>
</p>
</li>
{% endfor %}
</ul>
</section>
<section>{% include newsletter-subscribe.html %}</section>
</div>
</div>
</div>
60 changes: 32 additions & 28 deletions _layouts/post.html
Original file line number Diff line number Diff line change
@@ -1,45 +1,49 @@
---
layout: default
---
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
<article itemscope itemtype="http://schema.org/BlogPosting">

<header class="post-header">
<h1 class="post-title" itemprop="name headline">{{ page.title | escape }}</h1>
<p class="post-meta">
<header class="border-b-2">
<h1 itemprop="name headline"
class="text-center text-2xl sm:text-3xl font-extrabold text-slate-900 tracking-tight dark:text-slate-200"">{{ page.title | escape }}</h1>
<p class=" text-center py-2">

<time class="post__date" datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
<svg class="icons">
<time datetime="{{ page.date | date_to_xmlschema }}" itemprop="datePublished">
<svg class="inline h-6 w-5 fill-blue">
<use xlink:href="#calendar" />
</svg>
{% assign date_format = site.minima.date_format | default: "%b %-d, %Y" %}
{{ page.date | date: date_format }}
<span class="">{% assign date_format = site.minima.date_format | default: "%b %-d, %Y" %}
{{ page.date | date: date_format }}</span>
</time>
{% if page.author %}
&#8226; <span itemprop="author" itemscope itemtype="http://schema.org/Person" class="author"><span itemprop="name">{{ page.author }}</span></span>
{% endif %}</p>
&#8226; <span itemprop="author" itemscope itemtype="http://schema.org/Person" class="author"><span
itemprop="name">{{ page.author }}</span></span>
{% endif %}
</p>
</header>

<div class="post-content" itemprop="articleBody">
<div class="mx-auto py-8 prose md:prose-lg prose-headings:bold" itemprop="articleBody">
{{ content }}
</div>
</article>

{% if page.comments %}
<section>
<div id="disqus_thread"></div>
<script>
<section>
<div id="disqus_thread"></div>
<script>

var disqus_config = function () {
this.page.url = "{{site.url}}{{page.url}}";
// this.page.identifier = "{{page.url}}";
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://themoderndeveloper.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</section>
{% endif %}
var disqus_config = function () {
this.page.url = "{{site.url}}{{page.url}}";
// this.page.identifier = "{{page.url}}";
};
(function () { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://themoderndeveloper.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by
Disqus.</a></noscript>
</section>
{% endif %}
125 changes: 125 additions & 0 deletions _posts/2018-04-17-import-slug-into-woocommerce.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
layout: post
category: posts
title: Importing Custom Product Slugs / Permalinks into WooCommerce
author: Phil Oxrud
comments: true
description: Guide to adding Slug/Permalink Importing functionality to WooCommerce's Product Import tool
post_image: /assets/slug-rename-mapping.jpg
---

_NOTE: If you’re not a developer skip to the end to download the plugin that will let you add a “slug” column to the WooCommerce importer tool._

Recently I was tasked with transferring a few hundred products from an older [e-commerce platform](http://www.zen-cart.com) to WooCommerce. Thankfully WooCommerce provides a mechanism for importing products that are listed in a CSV file format.

The process ended up being fairly straight forward:

- Execute an SQL query on the older e-commerce platform to get a list of products and their attributes
- Export this list to a CSV file
- Write a script (I used Ruby) to convert the above exported CSV into a WooCommerce CSV format. You can see the WooCommerce CSV file specifications [here](https://github.com/woocommerce/woocommerce/wiki/Product-CSV-Import-Schema).
- Upload all product images
- Import the newly converted CSV file into WooCommerce using the builtin WooCommerce product import tool

While the above steps are certainly interested and probably deserve an article of their own this is not what this post is about..

The WooCommerce CSV file format does support most product properties, but for some unknown reason it does not support the individual product _Permalink_ (also known as “Slug") property. If you’re not sure what I’m talking about, the _Permalink_ is the custom url that you give your product.

For example: http://www.mysite.com/products/**my-custom-permalink.\*\*

Custom permalinks are a must for any site, especially e-commerce, so that we don’t end up with product urls like this: http://www.mysite.com/?p=324234

So it is very surprising that the WooCommerce team did not include permalink importing. If you’re importing a store with a few thousand products do they expect us to go in and manually edit the permalink property of each product?

Luckily WooCommerce does provide us with a nice API for adding custom properties to import from the CVS file. With the API we can customize the Product Importer tool to add the properties that we are missing. The documentation is available [here](https://github.com/woocommerce/woocommerce/wiki/Product-CSV-Importer-&-Exporter#adding-custom-import-columns-developers).

I used the API to add a “Slug” property support to the importer tool.

Additionally I added a “Slug” column to my CSV file, and adjusted my converter script described above to make sure the custom permalinks from the old e-commerce platform were under the “Slug” column in the newly generated CVS file.

Now let’s get to the code. Creating a custom “Slug” import column requires the following steps:

1. Add the new ‘Slug’ column to the importer internal data structure
2. Add the new column to the Importer GUI Automatic Mapping screen. This is the screen that lets users visually map CSV columns to WooCommerce product data fields. This step is optional.
3. Process the data that you imported. This is where you actually decide what to do with your newly imported data. In our case, use the data to update product Slug.

![add-slug-to-importer-diagram.png](/assets/slug-rename-steps.png)

WooCommerce provides filter hooks to accomplish each of the above 3 steps. As with all Wordpress hooks and custom code, our code will go to in the functions.php file.

## Add the Slug column to the Importer

Here is the code to add a “Slug” column to the Importer tool. It will then recognize a “Slug” column in your CSV files, but won’t know what to do with it just yet.

```php
function add_column_to_importer( $options ) {

// column slug => column name
$options['slug'] = 'Slug';

return $options;
}
add_filter('woocommerce_csv_product_import_mapping_options', 'add_column_to_importer');
```

## Add Automatic Mapping: column to CSV field

```php
function add_column_to_mapping_screen( $columns ) {

// potential column name => CSV column slug
$columns['Slug'] = 'slug';

return $columns;
}
add_filter('woocommerce_csv_product_import_mapping_default_columns', 'add_column_to_mapping_screen');
```

This part confused me at first. What it does is map the new column that we just added to the Importer tool, to a column in your CSV file.

In my case, since I created a “Slug” column in the importer tool, and also identically named “Slug” column in my CSV file this step was completely unnecessary.

However if the names do not match, you need to create a mapping.

Hopefully this diagram illustrates this point a little better.

![import_tool_mapping.jpg](/assets/slug-rename-mapping.jpg)

## Process the Imported Data

At this point the importer can pick up the new “Slug” field from the CSV file, and now we need to actually update our product with this slug.

I was worried that this might get tricky, however going through the WooCommerce source code I discovered that the slug updating functionality is already baked into WooCommerce with an easy to use set_slug method. Let’s see the code.

```php
function process_import( $object, $data ) {

if (!empty($data['slug'])) {
$object->set_slug($data['slug']);
}

return $object;
}
add_filter('woocommerce_product_import_pre_insert_product_object', 'process_import', 10, 2);
```

The process_import function will run on every row imported from our CSV file. After the row gets parsed and matched up with the Importer’s columns it get’s stored in the $data object, where the column names are the keys into the object. So $data[‘slug’] would give you the value of the slug in the current row being processed.

$object referes to the [WC_Product](https://docs.woocommerce.com/wc-apidocs/class-WC_Product.html) object. WooCommerce uses WC_Product instances to handle individual product data.

WC_Product has a nice

```php
set_slug(string $slug)
```

method that will set our slug for us.

## Source Code

If you'd like to see the completed source code, it is available on my [GitHub](https://www.github.com/poxrud/WooSlugImporter) page as a plugin.

## Plugin Download

If you simply want to add Slug importing functionality to your WooCommerce store, download the plugin below.

[Download Plugin](/assets/woo-slug-importer.zip)
Binary file added assets/coding-bg.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions assets/css/main.css → assets/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@tailwind components;
@tailwind utilities;

@import "pygments.css";

@font-face {
font-family: 'permanent_markerregular';
Expand All @@ -14,6 +15,7 @@
font-style: normal;
}


.hero__headline {
font-family: 'permanent_markerregular', Arial, sans-serif;
text-shadow: 2px 2px 3px rgba(0,0,0,0.9);
Expand Down
Loading

0 comments on commit 22f32bf

Please sign in to comment.