Creating a New User Onboard in WordPress

I'm finishing up a website for a private project which allows for people to register and create posts as members. The client wanted to have a specific onboarding flow which moved users from account creation to a terms page before finishing on a brief site overview.

I started by hooking into registration_redirect and pushing the user to a simple terms page.

add_filter( 'registration_redirect', 'new_user_redirect' );
function new_user_redirect() {
    return home_url('/complete-registration/');
}

This didn't work because the user was immediately redirected to the terms page instead of on their login. This meant my terms form (using Gravity Forms) couldn't collect the user_id field because it didn't actually exist yet.

To fix this, I hooked into the user_register action to capture the new user. There are several validation steps on the front and backend that I won't detail, but in the end, the user is written to the database and emailed a secure link to set their password. I created a new user meta key called is_first_login and set it to true. This was added when the user was written to the database.

update_user_meta( $user_id, 'is_first_login', true);

Now, I can check that key and send the user to the correct page when they log in.

add_action( 'login_redirect', 'redirect_user_on_login', 10, 3);
function redirect_user_on_login( $redirect, $request, $user ) {
    if(get_user_meta($user->ID, 'is_first_login', true)) {
        // Since it's the first login, set this to false
        update_user_meta($user->ID, 'is_first_login', false);

        // push to the terms page
        return home_url('/complete-registration');
    } else {
        // Check the user role and redirect appropraitely
        return (is_array($user->roles) && in_array('administrator', $user->roles)) ? admin_url() : site_url();
    }
}

If it is a new user, they are shown the terms page. Accepting the terms allows for accountability. Gravity Forms handles the next redirect when the user submits the terms form.

Change Course Role in Canvas via the API

Continuing my Canvas excursions...

We recently made a change where teachers could not manually add students to their courses. The change was made because manually enrolling a student breaks the grade passback to our SIS, which causes double the work to fix the problem in the first place.

But, this also affects manually-created courses that don't need the grade passback. One workaround is to add all students as a TA or teacher, but then you run into issues where students have access to grades.

The API doesn't allow you to directly change a user's enrollment status. You need to delete the user object from the course and then re-enroll to change the status. The change in the UI on the website does the same thing, but they make it look nice with a dropdown and it doesn't actually tell you that the user was deleted and re-added all in one step.

The nice thing about the API method is that you can set their enrollment state to active by default, which removes the course invitation notification and acceptance step for someone who is just having their status changed.

The example below is what I used to convert all students who were added as TAs back into Students. As always, I'm using the UCF Open Python library to handle the API calls.

from canvasapi import Canvas

canvas = Canvas('your_url', 'your_key')

course = canvas.get_course(course_id)

enrollments = course.get_enrollments(type='TaEnrollment')

for stu in enrollments:
    user_id = stu.user_id

    # the `deactivate` method takes a **kwarg to define what type of deactivation.
    # accepted strings: ['conclude', 'delete', 'deactivate', 'inactivate']
    stu.deactivate(task='delete')
    course.enroll_user(user_id, 'StudentEnrollment', enrollment_state='active')

This does wipe out the 'Last Activity' field in the People tab, so if that's important to you, make the change before the course is published. I made the change for a user, going from student to teacher and back with no loss of grade data, which was nice to see.

Making RSS Feeds for Instagram

tl;dr: I have a hacky proof-of-concept method for getting an Instagram account as an RSS feed. It uses Python and you can grab the source files here.


I'm not on Instagram because I got tired of only seeing three people's photos interwoven with ads. The problem is I still have friends who post frequently there and I feel like I should still be able to see those photos.

The wonderful thing about the Internet is that you can do things that weren't really meant to be done. Instagram (nor most other companies) provide RSS feeds anymore in order to force you into their platform. That's silly. I've been teaching myself Python and this seemed like a good way to flex some of my new powers.

Get that feed

Inspired by Andy Barefoot, who did some magic on his personal site with PHP, I decided to do the same using Python. What resulted was a command line program which can fetch any public Instagram account and create an XML document I could subscribe to.

I'm going to use Alane Levine as my guinea pig dog for this post. To create the feed, run:

python subscribe.py cogdog

where the argument is the username of the account. I'm using a handly lbrary called PyRSS2Gen by Andrew Dalke to create the properly formatted feed. I ran the script and then threw it on my server and subscribed, just to see what would happen.

An RSS feed for an Instagram account.

The feed exists in real life.

evil cackle

Update that feed

Instagram only shows 12 photos at a time. If I ran this script over and over, it would drop a photo from the feed each time it updated. That's no good.

I wrote up a second (notably more hacky) companion which takes almost the same form in order to update the feed rather than create one from scratch:

python update.py cogdog

This little guy looks for the existing XML doc and then fetches the user's Instagram page yet again. Instead of writing everything, it only writes things with timestamps newer than the most recent feed item. It's a little brute force, but hey, every tool can be a hammer if you swing hard enough.

Improvements

The subscribe script only loads those initial 12 photos. I may still go back and have it get the entire profile in the first go, but limiting it seems okay to me.

It's not general-purpose yet because you have to know how to install Python and several modules as well as have a web server to host the feeds on. I started finessing this into a small webapp which would do all the jobs, but my brain is stretched pretty far as it is.

If you want the source, it's in a GitHub gist and you can certainly tweak and improve. Let me know if you make changes or how I could do this better in the future.

I Took a Deep Breath and Contributed to an Open Source Project

I've been playing around with building websites and self-teaching myself (is that redundant?) web development languages. I feel like I've crossed some kind of line in the last 18 months where I can identify a problem, conceptualize a web-based solution, and then build it much more easily than I could in the past. I know that skill comes from experience, but it's nice to reflect back on my learning and see growth.

I built mostly for myself at the start. I run a simple [static homepage](https://ohheybrian.com) and a few small pages (see links in the right sidebar) for some small Google Apps Add Ons that haven't seen much love lately (shame on me). I've also kicked off a couple of projects for the district that have seen some daylight.

Most of this has been totally private. I would cruise [GitHub](https://github.com) from time to time for [interesting](https://github.com/bennettscience/tweetmineR) [projects](https://github.com/bennettscience/TimelineJS3) [to play with](https://github.com/bennettscience/gifify), but never really contributed to any of the projects.

Without getting too deep, Git tracks all changes made to files by anyone working on a project. It's terrifyingly complex, especially when you work yourself into an unsolvable tangle, but it does make seeing changes to code really, really handy. GitHub takes all of this information and puts it into one nice place for people from all over the world to see and work on together.

Contributing to a project on GitHub felt like walking into a room with a bunch of geniuses and asking what I could help with.

![I've made a huge mistake](https://proxy.duckduckgo.com/iur/?f=1&image_host=http%3A%2F%2F31.media.tumblr.com%2Fd86b14c7bd853f1db317007cd523605c%2Ftumblr_mjmxf5Uvnb1qduy16o4_500.gif&u=http://78.media.tumblr.com/d86b14c7bd853f1db317007cd523605c/tumblr_mjmxf5Uvnb1qduy16o4_500.gif)

Recently, I came across a really cool project called [Lychee](https://lycheeorg.github.io/) which is a self-hosted photo sharing site. It runs on any web server and gives you a nice, clean tool to upload, organize, and share photos. [I downloaded and got it running](https://photos.ohheybrian.com) after a couple days of [poking around](https://blog.ohheybrian.com/2018/11/forget-the-mac-mini-bring-on-the-raspberry/). While I was using it, I noticed a small error: toggling a setting didn't work because of a wrong function name. I was able to look at the code, find, and fix the error on my own site within about 20 minutes.

Then came the scary part - submitting a fix to the problem.

I had done this only once, and it ended with the owner saying, "Thanks but no," which was a bummer and a little embarrassing. I was about to submit some code that would be used in a real, live app that people downloaded and installed on their machines. My name would be attached to this particular fix (git tracks who changes what). I was worried that my fix would magically not work as soon as I posted it.

All of this fear was built up in the _perception_ of what community-based projects require. Most of the anxiety I felt was because of the image of this world being closed off to people who could speak the language or do things perfectly (sidebar: this is true, especially in the [tech sector](https://www.recode.net/2018/2/5/16972096/emily-chang-brotopia-book-bloomberg-technology-culture-silicon-valley-kara-swisher-decode-podcast), and GitHub isn't immune. More on that later.). But, things break. Problems need to be solved. And I'd just solved one that could help someone else. And anyone can submit fixes to code, so I decided to [give it a try](https://github.com/LycheeOrg/Lychee/pull/112).

Nothing scary happened. The site manager sent me a quick thanks and implemented the code.

I started looking through some of the other issues on this particular project and noticed one that I felt like I could help with, even if it was just exploring possibilities. Someone wanted to be able to tag photos with Creative Commons licenses using a menu, not just throwing tags into the `tag` field.

Long story short, this was a much bigger problem. There were easily six to ten non-trivial changes necessary to make this update happen. Over the last four days, I've worked hard to learn about the app's structure, the languages used to make the whole thing tick, and how to implement this feature without breaking the entire site. Today, [that feature was added](https://github.com/LycheeOrg/Lychee-front/pull/18) after some back and forth on code structure with the main author.

I'm a hobbyist. I don't plan on going into development full time because I like teaching too much. But there are definite things I learned through this weekend's project:

- **Submitting requests to update an app is just that: a request**. I'd built up this idea that submitting code - even flawed code - to a project would be a waste of that person's time. But really, providing a starting point can help make progress on complex issues.
- **Take time to look at the culture around the project**. The first one I'd submitted to was a single guy who wrote a script to make something easier. In hindsight, submitting a suggestion for a feature wasn't in his interest, so he had every right to say no. Larger projects have more eyes on requests and dialog is easier to kick off.
- **Pay attention to the project's style guides and code requirements**. I was able to save myself a major headache by going thoroughly through my code to make sure it was at least up to the grammatical standard set by the project owners. That way, we could focus on implementation discussion rather than form.
- **If you can't submit code, submit bug reports or provide insight in other discussions**. The documentation pages are a great way to get involved as well. Bugs need to be reported and instructions don't write themselves. Both are low-barrier entry points and can help get you established as a good-faith contributor to the community.
- **Take a big breath of humility**. I didn't submit my request thinking it was the best solution, only that it was a _potential_ solution. After I submitted, we spent a day going back and forth, tweaking items here and there before adding it to the core files. [And there was _still_ a bug we missed!](https://github.com/LycheeOrg/Lychee/issues/120) Code is written by people, and people make mistakes, so plan on your code having mistakes. Know that you will get feedback on whatever you submit, but look at the goal and avoid taking it personally.

---
_Featured image is an [Unsplash photo](https://unsplash.com/photos/hokONTrHIAQ) by [Steve J](https://unsplash.com/@steve_j)._

Link Two Google Docs for Translating

An interesting Stack Overflow question popped up about auto-translating one document into another when edits are made. Most Google services have things called “triggers” which run functions after some kind of event – opening the doc, making a change, etc.

A Google Doc does not have access to the onEdit trigger, so there is no way to automatically run the translation unless you put it on a timer trigger, and that’s a waste of resources, especially if you’re not constantly updating the document. But, you can link two documents together via the ID and push changes made using a custom menu.

Grab a copy of the template with instructions.

Source:

var ui = DocumentApp.getUi();
var docProps = PropertiesService.getDocumentProperties();

function getText() {
  var doc = DocumentApp.getActiveDocument();
  var body = doc.getBody();
  var array = [];

  array.push(body.getText());

  sendTheText(array);
}

function sendTheText(e) {
  if (docProps.getProperty("childId") == "") {
    var prompt = ui.prompt("Child doc ID", ui.ButtonSet.OK);
    var response = prompt.getResponseText()
    docProps.setProperty("childId", response);
  }
  var childDoc = DocumentApp.openById(docProps.getProperty("childId"));
  var es = LanguageApp.translate(e, 'en', 'es');
  childDoc.getBody().clear();
  childDoc.getBody().appendParagraph(es);
}

function clearProps() {
  docProps.setProperty("childId", "");
}

function onOpen() {
  ui.createMenu("Custom Translate").addItem("Run", "getText").addItem("Clear IDs","clearProps").addToUi();
}