Joho the Blogphp Archives - Joho the Blog

January 26, 2016

Oscars.JSON. Bad, bad JSON

Because I don’t actually enjoy watching football, during the Pats vs. Broncs game on Sunday I transposed the Oscar™ nominations into a JSON file. I did this very badly, but I did it. If you look at it, you’ll see just how badly I misunderstand JSON on some really basic levels.

But I posted it at GitHub™ where you can fix it if you care enough.

Why JSON™? Because it’s an easy format for inputting data into JavaScript™ or many other languages. It’s also human-readable, if you have a good brain for indents. (This is very different from having many indents in your brain, which is one reason I don’t particularly like to watch football™, even with the helmets and rules, etc.)

Anyway, JSON puts data into key:value™ pairs, and lets you nest them into sets. So, you might have a keyword such as “category” that would have values such as “Best Picture™” and “Supporting Actress™.” Within a category you might have a set of keywords such as “film_title” and “person” with the appropriate keywords.

JSON is such a popular way of packaging up data for transport over the Web™ that many (most? all?) major languages have built-in functions for transmuting it into data that the language can easily navigate.

So, why bother putting the Oscar™ nomination info into JSON? In case someone wants to write an app that uses that info. For example, if you wanted to create your own Oscar™ score sheet or, to be honest, entry tickets for your office pool, you could write a little script and output it exactly as you’d like. (Or you could just google™ for someone else’s Oscar™ pool sheet.) (I also posted a terrible little PHP script™ that does just that.)

So, a pointless™ exercise™ in truly terrible JSON design™. You’re welcome™!

Comments Off on Oscars.JSON. Bad, bad JSON

August 4, 2015

Posting to WordPress without WordPress

Perhaps you’d like to post to your WordPress blog from an app that isn’t WordPress.

I know I do. I write most of my posts in an editor (javascript + php) I’ve worked on for over ten years. Someday I’ll clean it up and post it at GitHub so you can all have a laugh. Meanwhile, it intermittently loses its ability to post straight to my blog, so I have to copy and paste the text into the WP editor. But I fixed it again today. So, here’s a tutorial for people at my level of non-technicality. (I got huge help from a post at HurricaneSoftware. Thanks!)

First, make sure that the file xmlrpc.php is installed where you’ve installed your WordPress blog software. This file comes from WordPress itself, and it should be there automatically. Check the permissions; I think it should be 644 but I am terrible at permissions.

I run my homegrown editor from my Mac, using the Apache web server that MAMP supplies. That lets me write blog posts even when I’m not online. That means the directory from which I’m running my JavaScript and PHP is on my hard drive. I keep these files in  /Applications/MAMP/htdocs/blogdraft/. (Blogdraft is the name of the folder in which my code resides.) To the web server, the address looks like this: /localhost/blogdraft/.

The operative part of this is your PHP file. Create an empty text file and name it, let’s say, postViaXmlrcp.php. For a first pass, it should look like this—and the brunt of this comes straight from HurricaneSoftware:

1

<?php

 
2

// Modified from:

3

// http://www.hurricanesoftwares.com/wordpress-xmlrpc-posting-content-from-outside-wordpress-admin-panel/

4

// Thanks!!

 

5

require_once(“IXR_Library.php.inc”);

6

$client->debug = true; //Set it to false in Production Environment

 
7

$title=$_REQUEST[‘title’];

8

$body=$_REQUEST[‘body’];

9

$keywords=$_REQUEST[‘tags’];

10

$category=$_REQUEST[‘categoryArray’];

11

$customfields=null;

 

12

$encoding = ini_get(“default_charset”);

13

$title = htmlentities($title,ENT_QUOTES,$encoding);

14

$keywords = htmlentities($keywords,ENT_QUOTES,$encoding);

 

15

$content = array(

16

‘title’=>$title,

17

‘description’=>$body,

18

‘mt_allow_comments’=>1, // 1 to allow comments

19

‘mt_allow_pings’=>1, // 1 to allow trackbacks

20

‘post_type’=>’post’,

21

‘mt_keywords’=>$keywords,

22

‘categories’=>$category,

23

‘custom_fields’ => array($customfields)

24

);

 

25

// Create the client object

26

$client = new IXR_Client(‘http://www.yourblog.com/myWP/xmlrpc.php’);

27

$username = “your-WP-username”;

28

$password = “your-WP-password”;

 

29

$params = array(0,$username,$password,$content,true); // Last parameter is ‘true’ which means post immediately, to save as draft set it as ‘false’

 

30

// Run a query for PHP

31

if (!$client->query(‘metaWeblog.newPost’, $params)) {

32

die(‘Something went wrong – ‘.$client->getErrorCode().’ : ‘.$client->getErrorMessage());

33

} else { echo “Article Posted Successfully”; }

34

?>

This PHP script relies upon another one, so you have to load it. “Require_once” will do so, and it will remember that it has done so during a session so you won’t waste computer resources reloading it every time you run this script.

You can get this script here. Right click on that link and choose “Save file as…” or however your browser puts it. Put it in the same directory as your PHP script. Make sure you name it “IXR_Librabry.php.inc.” Set its permissions. (See above.) Then leave it alone. 

7

$title=$_REQUEST[‘title’];

8

$body=$_REQUEST[‘body’];

9

$keywords=$_REQUEST[‘tags’];

10

$category=$_REQUEST[‘categoryArray’];

11

$customfields=null;

These lines read data that you’ve sent from the JavaScript that we haven’t written yet. It assigns them to some pretty obviously-named PHP variables.

Notice that we’re doing nothing with the $customfields variable. That’s because I don’t know what to do with it. I would have just deleted that line, but it scares me. And yet fascinates me.

12

$encoding = ini_get(“default_charset”);

13

$title = htmlentities($title,ENT_QUOTES,$encoding);

14

$keywords = htmlentities($keywords,ENT_QUOTES,$encoding);

htmlentities is a PHP function that makes sure that your HTML with all of its weird characters arrive without being translated into something more literal and wrong. Line 12 tells it which character encoding to use. I could have decided on one for you, but instead I’m just using whichever one you already use. We have already established I’m a coward, right?

15

$content = array(

16

‘title’=>$title,

17

‘description’=>$body,

18

‘mt_allow_comments’=>1, // 1 to allow comments

19

‘mt_allow_pings’=>1, // 1 to allow trackbacks

20

‘post_type’=>’post’,

21

‘mt_keywords’=>$keywords,

22

‘categories’=>$category,

23

‘custom_fields’ => array($customfields)

24

);

We are eventually going to be sending all of the content information to  WordPress via XMLRPC. This section packs an array (“$content”) with the information XMLRPC needs, attached to the keywords it loves. If you want to argue about it, take it up with XMLRPC.

26

$client = new IXR_Client(‘http://www.yourblog.com/myWP/xmlrpc.php’);

We now create a new client for the IXR script you downloaded. It wants to know where your xmlrpc.php file is, which should be where the rest of your WordPress files are folders are.

27

$username = “your-WP-username”;

28

$password = “your-WP-password”;

Fill in your WordPress username and password.

29

$params = array(0,$username,$password,$content,true); // Last parameter is ‘true’ which means post immediately, to save as draft set it as ‘false’

Now we’re making another array. This one includes the prior array ($content) as well as your username and password. And note the comment. Setting to “draft” is very useful when you’re playing around with these scripts.

31

if (!$client->query(‘metaWeblog.newPost’, $params)) {

32

die(‘Something went wrong – ‘.$client->getErrorCode().’ : ‘.$client->getErrorMessage());

33

} else { echo “Article Posted Successfully”; } ?>

This does the deed. No one knows how.  If it fails, it’ll pop up the error messages and kill it before it spawns evil. Otherwise, it sends back the message that it posted successfully.

You can test this PHP script by running it in your browser. If you’re running a local web server, you’d enter something like this: /localhost/blogdraft/postViaXmlrcp.php. (That’s assuming you put it in a folder called “blogdraft,” of course.) Check with MAMP or whatever you’re using for your web server for details.

But running this as-is won’t work because it’s expecting the content to be sent to it from the JavaScript we still haven’t written. So, comment out lines #7-11, and insert something like these:

$title=”TEST TITLE”;

$body=”<h1>Hello, world!</h1>”

$keywords=”tag1,tag2″

$category=array(“cat1″,”cat2”);

$customfields=null;

Replace the categories (“cat1”, “cat2”) with the names of categories that you actually use. Also, change “true” to “false” in line #29 so you’ll just produce drafts, not actually publish anything yet.

Now when you run this PHP file in your browser ( /localhost/yourLocalFolder/postViaXmlrcp.php), if should create a draft post. Check via the “All posts” page at your WP administration page to see if the draft got created.

When it’s working, comment out the four lines immediately above and uncomment lines #7-11.

The JavaScript

I’m going to pretend that you have some HTML page that has a text box where you can enter the content of your post, a similar box for entering the title, one for entering tags separated by commas, and checkboxes that list the categories you use. I’ll also assume that you use jQuery. So, your HTML might look soomething like this:

1

<!DOCTYPE html>

2

<html lang=”en”>

3

<head>

4

<meta charset=”utf-8″ />

5

<title>WordPress poster tester</title>

6

<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js”></script>

 

7

<script>

8

function postIt(){

9

// get the title

10

var titlecontent = $(“#titlebox”).val();

11

// get the body of the post

12

var bodycontent = $(“#contentbox”).val();

13

// get the tags

14

var tagscontent = $(“#tagsbox”).val();

 

15

// create array of categories

16

// get an array of all checkboxes in the div

17

var checks = $(“#categories”).find(“input”);

18

// create an empty array

19

var cats = new Array();

20

// go through all the checkboxes

21

for (var i=0; i < checks.length; i++){

22

// is this one checked?

23

if ($(checks[i]).is(‘:checked’)){

24

// if so, then push its value into the array

25

cats.push( $(checks[i]).val() );

26

}

27

}

 
28

// run the php

29

$.ajax({

30

type: “POST”,

31

url: “postViaXmlrcp.php”,

32

dataType: JSON,

33

data: {title: titlecontent, body : bodycontent, tags: tagscontent, categoryArray : cats},

34

error: function(e){

35

if (e.responseText.indexOf(“Successfully”) > -1){

36

alert(“Success! Post has been posted! Let the regrets begin!”);

37

}

38

else{

39

alert(‘Error posting blog via xmlrpc: ‘ + e.responseText);

40

}

41

}

42

})

 
43

}

 
44

</script>

 

 

45

</head>

46

<body>

47

<textarea id=”titlebox”>test title</textarea>

48

<textarea id=”contentbox”><h1>got some content here</h1></textarea>

49

<textarea id=”tagsbox”>tag1, tag2</textarea>

 
50

<div id=”categories”>

51

<input value=”business” type=”checkbox” checked>Cats

52

<input value=”dogs” type=”checkbox”>Dogs

53

<input value=”philosophy” type=”checkbox”>Phenomenology

54

</div>

 
55

<input type=”button” value=”Post It!” onclick=”postIt()”>

56

</body>

57

</html>

 

So, roughly, here’s what’s happening:

6

<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js”></script>

This loads jQuery from Google. Of course you could keep a local copy and include it that way.

8

function postIt(){

9

// get the title

10

var titlecontent = $(“#titlebox”).val();

11

// get the body of the post

12

var bodycontent = $(“#contentbox”).val();

13

// get the tags

14

var tagscontent = $(“#tagsbox”).val();

The postIt function begins by using jQuery to fetch the values entered into the three text areas. (Just in case you don’t know jQuery, “$(“#titlebox”) gets the element with the ID of “titlebox.” And if you don’t want to use jQuery, you can get the same result with: var titlecontent = document.getElementById(‘titlebox’).value.

16

// get an array of all checkboxes in the div

17

var checks = $(“#categories”).find(“input”);

18

// create an empty array

19

var cats = new Array();

20

// go through all the checkboxes

21

for (var i=0; i < checks.length; i++){

22

// is this one checked?

23

if ($(checks[i]).is(‘:checked’)){

24

// if so, then push its value into the array

25

cats.push( $(checks[i]).val() );

26

}

27

}

Creating an array of categories takes a little more work. Line #17 creates an array (“checks”) of all of the checkboxes in the div with the id “categories.” Lines #21-27 look at each of the checkboxes in that array. If line #23 sees that a particular checkbox has in fact been checked, then it puts the value of that checkbox into the created on line #19. (You want the value to be exactly the same as the name of the category in your WordPress installation. Also, remember that the checkbox’s value is not necessarily the same as the text displayed to the user.)

29

$.ajax({

30

type: “POST”,

31

url: “postViaXmlrcp.php”,

32

dataType: JSON,

33

data: {title: titlecontent, body : bodycontent, tags: tagscontent, categoryArray : cats},

34

error: function(e){

35

if (e.responseText.indexOf(“Successfully”) > -1){

36

alert(“Success! Post has been posted! Let the regrets begin!”);

37

}

38

else{

39

alert(‘Error posting blog via xmlrpc: ‘ + e.responseText);

40

}

41

}

42

})

Now we call the PHP script that we created above. We do this via AJAX, using the jQuery syntax, which is much simpler than the native JavaScript way of doing it. Lines #30-40  specify the communication the JavaScript will have with the PHP file.

Line #30: The “POST” here has nothing to do with posting a blog post. It’s stating what sort of transaction we’re about to have with the PHP script.

Line #31: This is the path to the PHP file we’re going to run. If it’s in the same directory as this HTML file, you don’t have to monkey with a path name.

Line #32: We’re going to pass data to the PHP script in the JSON way of expressing data.

Line #33: This creates the JSON we’re going to send. It’s all within curly brackets. There are four phrases, separated by commas. Each phrase consists of a keyword (which you can think of as being like a variable) and a value. We are free to make up whatever keywords we want, so long as those are the keywords we use in the PHP file to fetch the data that they label; see lines #7-10 in the PHP script above.

Line #44: If there is an error in the PHP, it will send back some information. There is also an equivalent “success:” function available. But I’m doing something wrong, because even when the PHP works and the blog gets posted, I still get an error message. If you go back to Line #33 of the PHP, you’ll see that if the PHP succeeds, it sends the message “Article Posted Successfully.” For reasons I don’t understand, that message shows up in the “error:” function of the AJAX. So, I check the message. If it has the word “Successfully” in it, the script alerts the user that the post has been posted. If it does not, on line #39 it posts an error message.

That’s it. If it doesn’t work, it’s because you’re doing something wrong,  starting with listening to me. Obviously I can’t help you since I don’t even know how this thing works.

Good luck!

3 Comments »

September 9, 2012

Beginner2Beginner: Javascript multi-file upload + PHP to process it

Much as I love being a hobbyist programmer, it can sometimes be frustrating as all get-out. Sometimes it’s just a bug because I made a dumb error (getting a variable’s scope wrong) or because I made an assumption about how something works (BBedit‘s hex dump does not show you the content of the file on the disk, but of the file in memory, including the line endings it’s transformed). But then there are the frustrations that come from not having the slightest idea of the basics. The worst are the basics that are so basic that the explanations of them assume you already know more than you do.

Welcome to the world of pain known as uploading multiple files using Javascript. For example, suppose you are writing an app that lets users take notes on an article using a plain old text processor. They can then upload those note files to some code that processes them, perhaps turning them into a database. Rather than having users upload one file at a time, you want to let them upload a bunch.

There are plenty of slick, free examples on the Web that provide beautiful front ends for doing this. Many of them I could get to work, but not exactly the way that I wanted. Besides, I was really really really confused about what happened after the front end.

So, after a lot of skullpounding and forehead slapping, here’s a guide. But please take seriously this warning: I barely know what I’m doing, and I’m undoubtedly doing this in the clunkiest fashion possible. I am very likely getting important things wrong. Some of them may be fatal, at least in the programmatic sense. (If you have a correction, please let me know. Note I may fix the code that follows, rather than doing the usual — and proper — Web thing of striking through the errors, etc.) Here goes….

To enable uploads, you’ll be writing code that will live in two places. The user interface code is in the HTML running in the user’s browser. The files are going to be uploaded — copied — to a web server. The code in the browser is going to be Javascript. The code on the server is going to be PHP, because Javascript is for browsers. (Oversimplification noted.) Those two places can be physically the same machine. If you’re using a Mac, a web server comes standard; if you don’t have a web server handy, there are bunches of free ones; that’s beyond the scope of this post.

After experimenting with many of the beautiful packages that are available, I gave up and went with the HTML5 solution. Modern browsers will have no problem with this. If you need to design for the world of old-fashioned browsers, then you need to find a competent explanation. In short: I love you, but go away! (Apparently, if you use the HTML5 solution, it will still be usable in lesser browsers, although it will allow users to select only one file at a time.)

HTML5 makes it ridiculously easy to do the user interface bit: you just tell it you want to allow multiple selections within a file-chooser dialogue. This comes from a post by Tiffany Brown:

<form action="processThem.php" method="post" enctype="multipart/form-data">
<input type="file" value="" name="upload[]" multiple>
<button type="submit">Upload!</button>
</form>

This will create an input field that when clicked will launch a file-browsing dialogue box in which the user can select multiple files. It then sends it to the PHP script called “processThem.php” on your server. The two key points to note are that the first line does the work of allowing multiple choices, and the “[]” in the name turns that variable into an array that will pass the entire list of user choices to the PHP script waiting on your server.

Here’s what it looks like, although this particular example won’t actually upload anything:



Now you have to create the “processThem.php” script (or whatever name you’ve specified) on your server. The uploaded files get placed in an array called $_FILES. But, they don’t get stored on the server for long: they are stored in a temporary folder from which they are automatically deleted after a little while. So, you need to process them, and quite possibly move them for permanent storage to a folder of your choosing. Here’s some sample PHP code from an anonymous commenter (“Me”) on the Tiffany Brown post:


<?php
$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

$upload_dir  = './tmp/';
$num_files = count($_FILES['upload']['name']);

for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . urlencode(basename($_FILES['upload']['name'][$i]));

    if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['upload']['name'][$i])) {
        print "I asked for an image...";
    } else {
        if (@is_uploaded_file($_FILES['upload']['tmp_name'][$i])) {
            if (@move_uploaded_file($_FILES['upload']['tmp_name'][$i], 
                $upload_file)) {
                /* Great success... */
                echo "hooray";
                //$content = file_get_contents($upload_file);
                //print $content;
            } else {
                print $error_message[$_FILES['upload']['error'][$i]];
            }
        } else {
            print $error_message[$_FILES['upload']['error'][$i]];
        }    
    }
}
?>

Let’s walk through this.


$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

In the first few lines, Me does us the favor of providing non-technical explanations of possible errors, so that if something goes wrong, it will be easier to know exactly what it was.


$upload_dir  = './tmp/';

Then Me designates a particular folder as the container for the uploaded files. Me chooses one called “tmp” in the same directory as the PHP script. (Make sure you have such a folder and the permissions are set, or, of course, create one with whatever name you’d like.)


$num_files = count($_FILES['upload']['name']);

Then Me gets a count of how many files were uploaded, and stores it in the variable $num_files. You tell that variable that you want the number of files that were included in “upload[]” in the form on your HTML. (You can use whatever name you want on that form, so long as you use the same one in your PHP.)


for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . urlencode(basename($_FILES['upload']['name'][$i]));

Then Me loops through all the files, assigning each one in turn to the variable $upload_file. But notice the weirdness of this part of the line:

$upload_file = $upload_dir . urlencode(basename($_FILES[‘upload’][‘name’][$i]));

First the easy parts. The baseline function returns just a file’s name without any path information; we want that because the point of this line is build a path to where the file will be saved in the folder you’ve set up for it. Also, I added the urlencode function in case the name of the file your user uploaded contains spaces or other characters that makes your server barf.

Now consider $_FILES[‘upload’][‘name’][$i]. It’s got those weird bracketed terms because $_FILES is an associative array. You can think of the usual sort of arrays as pairing a value with a number; give the array the number and it returns the value. In an associative array, values are paired with arbitrary keys (i.e., a word); give it the key and it returns the value. Here are the pre-defined keys for the associative array that gets sent to the PHP script when a user uploads files:

  • name: The file name of the uploaded file
  • type: Is it an image? A music file? etc.
  • size: The size in bytes
  • tmp_name: The crazy-ass name of the copy being stored on the server
  • error: Any error codes resulting from the upload

So, suppose you’re cycling through the array of uploaded files as in our example, and you want to get the name of the current file (i.e., file $i in the sequence):


$fname = $_FILES['upload']['name'][$i];

The [‘upload’] designates the array of all uploaded files. The [$i] pulls out of that array all of the information about one particular uploaded file, just like with ordinary array. The [‘name’] gets the value associated with that key for that particular file. As $i is incremented, you get the name of each file. If you wanted the crazy-ass temporary name, you would put in tmp_name instead of name, and likewise for the other parameters.


if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['upload']['name'][$i])) {
        print "I asked for an image...";
    }

Next Me optionally checks the name of the uploaded file for a particular set of extensions in case you want to limit the uploads to images (as in Me’s example) or whatever. Me is using regex to do the work, a function for which I am doomed to a copy-and-paste understanding.


  if (@is_uploaded_file($_FILES['upload']['tmp_name'][$i])) {

Next Me does a check to make sure that the file was actually uploaded and is not the result of malicious code. is_uploaded_file is a built-in PHP function. The preceding “@” suppresses error messages; I don’t know why Me did that, but I’m confident it was the right thing to do. While you are debugging, you might want to take the @ out.


move_uploaded_file($_FILES['upload']['tmp_name'][$i], $upload_file)

If the file passes that test, then Me moves it to the folder s/he designated. Note that we’ve already put the pathname to the storage folder into $upload_file.

Obviously, where the script has the comment “Great Success” you would put the code that does what you want to do to the uploaded file. I have added two lines — commented out — that get and print the content of the file; that gets ugly if the file isn’t some type of text file.

So, please let me know what I’ve gotten wrong here. (And maybe sometime I’ll give you some truly embarrassing code for returning the results of the PHP on the page that has the upload form on it.)

8 Comments »

August 22, 2012

When php will only read itself

Some little apps I’ve written for myself over the years refused to work on a Mac with Lion that I’ve started using at work. The problem was that any php invoked via Ajax would return only itself — that is, the return value would be the lines of the php script itself. This is not helpful.

I poked around a lot on the Net and couldn’t find anything relevant. The closest was the observation that this can happen if you begin your PHP scripts with just a <? instead of <?php, which seems awfully persnickety. Unfortunately, my scripts didn’t begin with the abbreviated form.

After getting some help by posting at Stackoverflow.com, it seemed that the machines that my scripts have worked on have PHP v.5.3.8, whereas my Lion computer for some reason was at PHP 5.3.1. So, I upgraded the php to 5.4. This page has easy instructions for doing so: http://php-osx.liip.ch. (If you want to know what version of PHP you have, in a terminal window type “php -v”.)

I’m not 100% sure that it was the PHP upgrade that did it and not some random other change or a change in the phase of the moon. But it’s working. And if you’ve been having the same weird problem with PHP, maybe it will work for you.

Comments Off on When php will only read itself

January 2, 2008

Beginnger-to-Beginner: Enabling php in Leopard

You want to see some nice tech writing? James Pelow at PHPmac explains how to enable the Leopard apache server to use php. It’s written so clearly, even I could follow the instructions! (Because he wrote this in 2005, you’ll want to change the references to php4 to php5.)

[Tags: ]

3 Comments »