Code to convert exported Chrome bookmarks HTML to JSON

When you can’t find an existing solution to do something “simple” like convert an exported Google Chrome bookmarks HTML to JSON, it helps to be a programmer so you can build your own.

Check out my public GitHub code repository at: https://github.com/jsnelders/chrome-bookmarks-converter

I also have it running live at: https://chrome-bookmarks-converter.netlify.com/

It’s quick and dirty and only took a couple of hours but it work.

[Vue warn] and browser caching

(Relates to: VueJS 2.x)

I just modified a VueJS project and received the following warning:

[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'id' of null"

found in

---> <Anonymous>
<Root>

 

I’d split code from 1 JavaScript (JS) and 1 corresponding HTML page into 2 JS and HTML files.

After refreshing the page, I received the above error.

 

There was no problem in my code.

The problem was I needed to do a hard refresh in the browser get and execute and the new and updated files.

[Vue warn]: v-bind without argument expects an Object or Array value

(Relates to: VueJS 2.x)

I received the following error:

[Vue warn]: v-bind without argument expects an Object or Array value

found in

---> <Anonymous>
<Root>

The error was caused by the following line of code:

<input type="text" class="form-control" id="Title" v-bind="post.Title">

My mistake:

I used v-bind but I should have used v-model.

The correct code is:

<input type="text" class="form-control" id="Title" v-model="post.Title">

Get started with MongoDB and PHP with XAMPP on Windows

Getting started with MongoDB on Windows and developing it with PHP turned out to be quite easy.

You can create a MongoDB database “in the cloud” for free with MongoDB’s Atlas platform – https://cloud.mongodb.com. No credit card required and you’re up and running in under 10 minutes (most of that time is waiting) after filling in a simple form fill and a few button clicks.

Next you need to setup XAMPP if haven’t already. Follow my post Setting up XAMPP for WordPress Development on Windows for details on how to do that. That gives you Apache and PHP running capability.

Finally you need to install MongoDB drivers for PHP under XAMPP. I use https://www.configserverfirewall.com/mongodb/install-mongodb-php-driver-ubuntu-windows/#install-php-mongodb-driver-on-windows to figure that out. The steps are:

  1. Download the drivers for Windows from https://pecl.php.net/package/mongodb and extract the archive (select the “DLL” link for the version you want, scroll to the bottom of the next page, then download from the “DLL List” section for your version of PHP and OS architecture.
  2. Copy the extracted php_mongodb.dll file into <drive>:\xampp\php\ext folder.
  3. Open XAMPP php.ini and add the line: extension=php_mongodb.dll.
  4. Restart the Apache server in XAMPP. You’re good to go.

 

MongoDB Atlas connection string in PHP

If you run MongoDB locally you’ll have PHP code something link this to establish a connection:

<?php
$connection = new MongoDB\Driver\Manager("mongodb://localhost:27017");
var_dump($connection);
?>

If you connect Atlas use a connection string something like:

new MongoDB\Driver\Manager("mongodb+srv://<db-username>:<db-password>@azure-westus-1-fzl9p.azure.mongodb.net/test?retryWrites=true&w=majority");

Where <db-username> and <db-password> are the username and password you created in Atlas.
“azure-westus-1-fzl9p.azure.mongodb.net” is for my the specific Mongo instance – it will be different for you.

The connection string between the double-quotes is the same as was generated when you go into your collection, select the “Connect” button and follow the prompts.

JavaScript (.js) file interpreted as MIME type text/html

I had situation with a JavaScript (.js) file seeming to load OK but in reality it was a misdirect.

Using Chrome, I inspected the Network tab in Developer Tools and couldn’t see an error with the file load. But on closer inspection, I noticed the Status was a “302” and the Type was “text/html” as follows (I was expecting a “200” Status and “script” mime Type).

It was luck that I found the answer:

I was loading the JavaScript file using the path “/inc/assets/js/vueks/vue-2.6.10.js”.

But I had a spelling mistake in the path. It should have been: “/inc/assets/js/vuejs/vue-2.6.10.js”.

I changed “vueks” to “vuejs” and the script loaded successfully.

 

The strange thing is the incorrect path didn’t give me a “404” (Not Found) response. Instead I received a “302 Found”, where clearly the file was not found.

My guess is I was loading it in a WordPress site, and while the file was not found I do have code internally to redirect to a “/not-found” page that may have returned some form of response (even though it contained no content). That or some headers were returned which confused Chrome.

(I haven’t tried to replicate this other browsers yet.)

PHP $_POST not populating from jQuery (JavaScript) $.ajax() POST

Problem

I was trying to submit data from a web page to a back-end API in PHP using jQuery’s AJAX (ie. $.ajax()).

But the $_POST variable in PHP didn’t contain the values I was posting.

 

Solution

The problem is I was encoding the POST body data the wrong way in JavaScript.

I was using JSON.stringify(data), where “data” is a JavaScript object or array.

What I needed to do what manually create a string of key1=value1&key2=value2&keyN=valueN pairs.

Example Code from my submit function:

        //sendData = JSON.stringify(data);  //<-- This doesn't work ...

        // ... but this does.
        sendData = "";
        for (var key in data) 
        {
            if (sendData != "") sendData = sendData + "&";
            sendData = sendData + key + "=" + data[key];
        }

	var ajaxRequest = {
		url: url,
		type: "POST",
		data: sendData,
 		dataType: dataType,

		success: function(data, textStatus, jqXHR)  { successCallback(data, textStatus, jqXHR ); },
		error: function(jqXHR, textStatus, errorThrown) { errorCallback(data, textStatus, errorThrown); }
	};

	$.ajax(ajaxRequest);

 

What I searched for (which didn’t help much):

Search “$_POST empty from jquery”: https://duckduckgo.com/?q=%24_POST+empty+from+jquery&amp;atb=v147-1&amp;ia=web

https://stackoverflow.com/questions/1650516/jquery-ajax-method-post-but-post-empty

https://stackoverflow.com/questions/16360008/jquery-post-post-empty

https://stackoverflow.com/questions/54039285/ajax-form-posting-to-php-script-but-post-is-empty-in-php-script

https://stackoverflow.com/questions/1650516/jquery-ajax-method-post-but-post-empty/49948834#49948834

Git: Reset a local branch to last remote commit, remove unstages files, and fix file permissions

I use the Codeanywhere online IDE for my WordPress development. Sometimes I screw up something I’m testing (usually when I try to manipulate the filesystem with PHP) and need to reset my local branch to the last clean commit I sent to remote.

(Note that I said “remote”. I don’t trust local commits. I continually push to remote to have a safe restore point, especially if I need to do a full delete of my local branch. Yes, it has happened more than once.)

Occasionally I find folder permissions screw up as well and I need to reset via the bash command line.

The following commands are what I always come back to.

Note: The “$” in the command below represent the command-line prompt. The actual commands follow it.

 

Reset a local branch to the the last remote commit of the branch

$ git reset --hard HEAD

 

Clean up any uncommitted folders/files that the reset didn’t remove

$ git clean -f -d

 

Set full permission recursively on a folder and all files/sub-folders

Change directory (cd) to the parent folder of the folder you want to set permission on.

$ sudo chmod -R 777 vylesk-module-z1

 

Recursively delete a folder and all files/sub-folders

Change directory (cd) to the parent folder of the folder you want to delete.

$ rm -rf vylesk-module-z1

Note: The “f” means you are not prompted to delete each file/folder.

Bash: Remove file and folder with Windows style directory names and backslashes

The Problem

While working with zip files in WordPress (I’m setting up my own update server and package update process), I manually created a zip file in using 7zip in Windows (my primary operating system) and extracted it via PHP in a Codeanywhere Ubuntu workspace.

The problem was the zip file was created containing Windows paths, so I ended up file with a file name “vylesk-test-plugin\vylesk-test-plugin.txt” and directory name “D:\Temp\php\vylesk-test-plugin\vylesk-test-plugin.1.1\vylesk-test-plugin\“.
Notice the backslashes and “D:” drive path.

Removing the file and directory was a problem.

Attempting to perform a simple rm and rm -rf resulted in:

rm: cannot remove './vylesk-test-plugin\vylesk-test-plugin.txt': Permission denied
rm: cannot remove '_temp/vylesk-test-plugin.1.1/D:\Temp\php\vylesk-test-plugin\vylesk-test-plugin.1.1\vylesk-test-plugin\': Permission denied

I tried changing file permissions using chmod and that also resulted in:

chmod: changing permissions of 'vylesk-test-plugin.1.1/D:\Temp\php\vylesk-test-plugin\vylesk-test-plugin.1.1\vylesk-test-plugin\': Operation not permitted
chmod: changing permissions of 'vylesk-test-plugin.1.1/vylesk-test-plugin\vylesk-test-plugin.txt': Operation not permitted

 

The Solution

The solution turned out to be very simple. I just needed to prefix the command with sudo.

So I ended up with:

sudo rm vylesk-test-plugin\\vylesk-test-plugin.txt
sudo rm -rf 'D:\Temp\php\vylesk-test-plugin\vylesk-test-plugin.1.1\vylesk-test-plugin\'

The file and directory then deleted successfully (and I didn’t need an su password, which I don’t know anywhere because it’s in a virtual environment I don’t control).

Note 2 other elements of the commands:

  • In the first ‘rm’ line the “\\” (double-backslash), because backslashes need to be escaped.
  • In the second line I surrounded the directory path with single quotes, an alternative to the double-backslash.

PHP: “Fatal error: Can’t use function return value in write context in …”

I just received the following PHP error.

Fatal error: Can’t use function return value in write context in /path/to/file.php on line 595

And I had no fucking idea what the problem was. I had a “sense” of it, but I didn’t understand the “in write context” part (that’s what happens when you get geeks to write error messages).

The line causing the error was this:

if (strtolower($append_value) = strtolower($base_value))

It’s one of those classic programmer errors that is in your face but is easily lost in the code.

Here’s the fixed line

if (strtolower($append_value) == strtolower($base_value))

Yeap – I missed an equals sign to make it an equality expression rather than an assignment (i.e. “=” changed to “==”).

 

Interestingly, this error was thrown without actually being executed. It must have been while the script was being parsed. I’m using PHP 7.3 in WordPress in the Codeanywhere online IDE.

PHP: Recursively zip a folder/directory structure

Status: Draft (31 May 2019)

This is a work in progress. I want to record my working as I go.


What I Want

I need to zip a folder structure – ultimately, theme and plug-in folder in WordPress – using PHP.

I’m currently developing and testing on a Windows 10 PC. Ultimately it will need to work in Linux (Ubuntu), but the first step is to it working in Windows.

I started with solutions from https://gist.github.com/MarvinMenzerath/4185113/72db1670454bd707b9d761a9d5e83c54da2052ac and https://stackoverflow.com/questions/1334613/how-to-recursively-zip-a-directory-in-php but the zip file was being created, however, not files were added.

I realised the problem was the relative path for folders/files within the zip. My solution below is a modification of the referenced solutions, where I strip the source path from the fill file path of each folder and file to give the relative path to the source.

So far this works in Windows, next step is to test in Linux. I’m actively working the problem so stay tuned.

 

I created a file “D:\Temp\php\test_zip.php” with the code below.
I execute with the following command (I have XAMPP installed with includes PHP): “D:\Temp\php>D:\xampp\php\php.exe test_zip.php”.

The Code

(Aide: I used https://www.freeformatter.com/html-escape.html to convert raw PHP code to something I can past in between <pre> tags in the Classic WorPress content editor).

<?php
// Make sure the script can handle large folders/files
ini_set('max_execution_time', 600);
ini_set('memory_limit','1024M');

// Start the backup!
Zip('D:\\Temp\\php\\Source\\', './compressed.zip');
Zip('D:\\Temp\\hildas_prod_logs\\', 'D:\\Temp\\php\\backup-test.zip');
testOne('D:\\Temp\\php\\Source\\debug.log', 'D:\\Temp\\php\\compressed_testOne.zip');

echo "Finished" . "\n";


// A quick, reduced test.
// This works in Windows.
function testOne($source, $zipTo)
{
	$zip = new ZipArchive;
	if (file_exists($source) === false)
	{
		echo "Sourece does not exist" . "\n";
		return;
	}
	
	if ($zip->open($zipTo, ZIPARCHIVE::CREATE) === true) 
	{
		$zip->addFile($source, 'source\newname.txt');
		$zip->close();
		echo "OK" . "\n";
	} 
	else 
	{
		echo "FAILED" . "\n";
	}
}


// This works in Windows.
// Source and inspiration: https://gist.github.com/MarvinMenzerath/4185113/72db1670454bd707b9d761a9d5e83c54da2052ac - Marvin Menzerath. (http://menzerath.eu)
// Additional source and inspiration: https://stackoverflow.com/questions/1334613/how-to-recursively-zip-a-directory-in-php
function Zip($source, $destination)
{
    if (!extension_loaded('zip')) 
	{
		echo "Zip extension not loaded\n";
        return false;
    }
	
	if (!file_exists($source)) 
	{
		echo "Source not found:" . $source . "\n";
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) 
	{
		echo "Zip not created or opened\n";
        return false;
    }
	
	$raw_source = $source;

    $source = str_replace('\\', '/', realpath($source));
	
	echo "Raw source: " . $raw_source . "\n";
	echo "Clean source: " . $source . "\n";

    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
		
		$sourceWithSeparator = $source . DIRECTORY_SEPARATOR;
        foreach ($files as $file)
        {
            $file = str_replace('\\', '/', $file);

            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;

            $file = realpath($file);

            if (is_dir($file) === true)
            {
				$dir_path = str_replace($sourceWithSeparator, '', $file . DIRECTORY_SEPARATOR);
				
				echo "Directory: " . $file . " (Path: " . $dir_path . ")\n";
                //$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
				
				$zip->addEmptyDir($dir_path);
            }
            else if (is_file($file) === true)
            {
				$zip_relative_path = str_replace($sourceWithSeparator, '', $file);
				
				$zip_relative_path = remove_from_start($zip_relative_path, $raw_source);
				
				echo "File: " . $file . " (Path: " . $zip_relative_path . ")\n";
                //$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
				//$zip->addFile($file, str_replace($source . '/', '', $file));
				$zip->addFile($file, $zip_relative_path);
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}



function remove_from_start($full_string, $prefix)
{
	if (substr($full_string, 0, strlen($prefix)) == $prefix) 
	{
		$full_string = substr($full_string, strlen($prefix));
	} 
	
	return $full_string;
}

?>