Get a Free 250 MB Upgrade by Clicking the “Get Started” Tab in Dropbox

dropbox_gift I’ve been a happy Dropbox user since the private-beta was released a couple years ago. It’s by far my favorite file storage and syncing service, and it gives me peace of mind about backups of critical files.

If you’re an old-timer like me, you may not have noticed the Get Started tab when you log into your Dropbox web account. Click it and walk through the basic usage tutorial that it presents. When you’ve finished, you may receive a free 250 MB storage upgrade. It worked for me. More storage space is always appreciated.

If you don’t have a Dropbox account yet, you can receive two gigabytes of storage for free plus an extra 250 MB by using this signup link.

Basic Web Security

Web security is a huge topic, and this article only intends to cover some of the most basic issues and increase awareness of how carelessness or ignorance can lead to exploits. Ultimately, what you don’t know can hurt you, so it’s in your best interest to learn as much as you can about your site and the technologies it relies on. Here’s a brief run-down of some fairly common mistakes I’ve come across and what you can do to either avoid them or lessen your vulnerability.

Suspected Malware Site!
You've been F'd in the A!

Make Rolling Backups

If you do nothing else, make sure you are backing up your site and its databases. So long as you know if/when your site fails (or is hacked) and have the ability to roll back to a known good state, you have little to fear.

Make SURE you practice restoring a site from your backups! An untested backup is worthless. Nothing is worse that THINKING you have a viable backup only to discover that it is actually corrupted.

As your backup needs mature, consider storing them offsite, e.g. on Amazon’s S3 service.

I’m supplying two of my most-used backup scripts for those in need. These are fairly simple, but they will work on shared hosts. They will backup a web site’s files and its databases.

Be Diligent about Passwords

You’ve probably heard this a hundred times before, but it is really important:

  • NEVER use the same password twice! Ever!
  • Avoid short passwords! Length before strength. Try mixing up combinations of smaller units, e.g. “AlphaBetaCharlie” instead of “abc”. Be creative. You can have strong passwords that are easy to remember!
  • Store your passwords in a safe place, e.g. in a password manager like KeePass or in an encrypted disc image
  • Change your Passwords Frequently! Literally, put this in your calendar so you remember to do it on a periodic basis. This helps avoid brute-force attacks.
  • As a secondary line of defense, you can put an .htaccess password on your manager pages. All this does is slow down a brute-force attack by forcing the attacker to crack an additional password.

Password Protecting Directories using .htaccess

This adds an additional layer of authentication to a site or to a page; it’s not meant to substitute for more robust firewall rules or active filtering, but it is easy to set up.

First, create a username and password in a file that .htaccess can read. This can be done on the bash command line using the htpasswd command:

htpasswd -c /path/to/htpassword/file name_of_user

Next, add the following to your .htaccess file. Be sure you reference the file you just created above:

AuthName "Protected Area"
AuthGroupFile /dev/null
AuthType Basic
AuthUserFile /path/to/htpassword/file
Require valid-user

For some more detailed information on .htaccess passwords, see this page.

Stay Patched

Make sure you’ve got an updated version of your operating system, your scripting language (PHP), your database (MySQL), and your software (WordPress, MODx, etc.). Sometimes applying patches can be scary, but it’s a lot less so if you’ve got those rolling backups in place!

I want to make mention of a very useful tool: SimpleScripts. It’s available on Bluehost accounts; it provides one-click installs of many web software packages (like WordPress and MODx) and it will alert you to update them when you log into your cPanel. It’s a real time-saver!

Clean your Room!

  • Do not install superfluous/untrusted software on your site! Don’t go dumpster diving for code that’s going to end up on a public-facing web site!
  • Shut down services you’re not using (e.g. blog posts) because it takes more time to secure them.
  • Do not store backups or sensitive data inside your document root!
  • Encrypt sensitive data
  • Check permissions.
Proper web site folder structure
Don't put backups or database dumps in your document root!

Make sure you organize your site’s files in a way that ensures that only you have access to sensitive data like backups or database dumps. These should NEVER be stored in the publicly viewable document root!

Cross Site Scripting

Behold the terror that is:

<?php print $_GET['x']; ?>

That code should NEVER be used on a public site because it effectively gives free access to the public to put whatever they want on your site! This type of code often sneaks into pagination links or into code that re-populates forms, e.g.:

<input type="text" name="myfield" value="<?php print $_POST['myfield']; ?>" />

where the ‘myfield’ variable contains something like:
" /> <script src=""></script>

For a list of values you might want to try pasting into form fields to see if they are secure, check out this great cheat sheet at

In the end, be REALLY aware of any user-submitted data. Users can put their own data into ANY form field, and into any cookie, so anything in the $_GET, $_POST, or $_COOKIE arrays (and also the $_REQUEST array) is inherently insecure and should be carefully filtered. These are like the STDs of the web!!!

For articles about web hacks and some good real-world examples, check out other articles on

Don’t Take Cookies From Strangers!

If you thought cookies were somehow immune to the area of security threats, they are not! It’s easy to write your own! The Firefox Web Developer plugin lets you easily create your own cookies. This is great as a web developer, but it can also be a sneaky tool for a hacker to introduce unintended code to your application, so filter cookie contents as carefully as you would the user-submitted forms.

Cookies also store the PHP session id; all $_SESSION data is stored on the server, but the unique key that associates that data with the user is stored in the user’s cookie. If one user authenticates, it’s possible for another user to make requests using that $_SESSION id! Especially with applications that require a login, it’s good practice to get a new session id using session_regenerate_id().

Filtering data

Every time you submit form data, you should write your regular expressions very carefully so your application accepts ONLY clean, valid data. In general, if you can keep input as simplistic as possible, it tends to be easier to secure. Integer only inputs are “safer” than alphabetic inputs. Alphabetic input is safer than input that accepts HTML tags, and so on.

Consider the following filters:
Type-Casting to force integer only values:
$x = (int) $_GET['x'];

Alphabetical Characters Only:

// Accepts only letters a-z (case insensitive)
function alphabetical($str)
if ( preg_match('/^([a-z])+$/i', $str) )
return $str;
return FALSE;

For PHP coders, get familiar with the preg_replace() function: it offers a more standard regular expression syntax (the often emulated Perl syntax). Also have a look at the strip_tags() function.

Frame Buster

A common trick used in phishing scams or to perpetrate click fraud involves iframe-ing a site. Basically, the “trick” relies on the HTML iFrame tag to make one site display the contents of another without being obvious to the casual user.

One partial solution to this common attack vector is to use some simple javascript that checks to ensure that the page the parent page and not being iFramed:

if (top.frames.length!=0)
// -->

Do a Google search for “frame buster javascript” to find other examples.

SQL Injection

This is a very broad topic, and there are numerous ways that SQL-injection might be used to compromise a site, but they all rely on the same principle: you construct strings of SQL statements and issue them against your database. If a malicious user is able to put his own data into one of those strings, it’s possible that a user can execute queries on your database that you never expected. This often gets back to form-validation and the ever important task of filtering user-submitted data!

Here’s a not-so-hypothetical PHP example:

$username = $_POST['username'];
$sql = "SELECT * FROM `users` WHERE `username`='$username'";

where $username contains something like:
'; INSERT INTO users (username, password) VALUES ('hacker_dude', MD5('xxxxxx') )

When that executes, 2 different queries are sent to the database instead of one, and if your’e not careful, it can allow an attacker to gain access to parts of your site and delete or steal data.

One strong line of defense against this type of attack is using a robust database driver that allows for the use of prepared statements (available since MySQL 4.1): where you prepare a statement once, then execute it multiple times with only certain defined variables changing. What this does is it allows you to define your query and only let the user supply the variables to be used in that query. This is a much more sensible option than letting the user essentially construct the query from scratch.

More mature database driver libraries (such as phpmysli) will allow you to use prepared statements.

You should also get familiar with escaping quotes in your database queries; this isn’t anywhere as effective as using prepared statements, but sometimes you have to use statements that aren’t prepared, so get familiar with how to escape strings before sending them to the database. In PHP, use the mysql_real_escape() function.

Finally, consider setting up special database users and roles to handle different types of queries. If a query is hijacked, it can only execute with the same permissions as the database handle. In other words, you wouldn’t grant delete or insert permissions to a database handle that was used only for selecting data. It’s more work to set up your database handles this way, but it may help prevent an attack from succeeding.


Watch your cornhole. There’s a lot going on a web site, and there are a lot of ways to abuse the technologies that run them. If you understand how the exploits occur, you’ll be better prepared to prevent them.

Book Announcement – WordPress 2.9 e-Commerce (plus a giveaway)

Book cover - WordPress 2.9 e-Commerce In case you’ve been wondering why my number of posts dwindled in the last year, here is the reason: I’m pleased to announce the release of my first book, WordPress 2.9 e-Commerce, published through Packt Publishing.

As the name implies, this book works through the marriage of the popular WordPress software and the WP e-Commerce plugin offered by Like most of my writings, it’s a tutorial, though on a much grander scale than a simple post. 🙂

You can read all about it on Amazon, but suffice to say that this book is aimed toward anyone who wants to sell either physical goods or digital downloads using the familiar WordPress software. It’s a comprehensive overview, but I especially spend a lot of time discussing the nitty-gritty of payment setup (both PayPal and Google Checkout) as well as the myriad of shipping options and how to handle them.

It was exciting enough to send in the final revision earlier this year, but holding the complete book in my hands is a feeling beyond words to describe. I’m proud of this book, and I hope it helps budding entrepreneurs and hobbyists alike.

Book Giveaway

And now I’d like you to hold the book in your hands! I have two copies to give away, and will gladly mail them at my expense to two readers. To enter, all you have to do is leave a comment on this post. On Monday, 19 April 2010, I will randomly select two winners and notify them. I will ship worldwide, so anyone can enter.

Good luck!

Comparison of Online Email Marketing Companies

If you work on commercial web-sites, eventually you find the need to add email marketing services such as newsletters and special advertisements. The article compares 4 services: Campaign Monitor vs. Constant Contact vs. Mail Chimp vs. Topica in several categories, primarily templating abilities, the usability, and the documentation. This review does not cover the reporting capabilities of these products, but all of them offer the standard set of success metrics.

DISCLAIMER: The review only reflects my own experience using each product for about an hour each; I’m not affiliated with any of these companies.

Campaign Monitor

Price: Pay as you go: a flat delivery fee of $5, plus 1 cent for each recipient.

Templates: HIGH MARKS. You can get started with a library of their own templates, and you can easily customize the HTML and CSS via their custom tags, e.g.
<$title link='true' default='Enter Title Here'$>
They provide a good cheatsheet and plenty of template samples.

Limits: only 10 custom fields allowed on email forms.

API: HIGHEST MARKS. Very well documented, code samples of each method in various languages. Like Mail Chimp, Campaign Monitor also offers plugins to various CMS’s including WordPress, Drupal, Joomla, Expression Engine etc.

Demo: Yep, try it totally free. You only pay when you send an email.

Summary: Very easy to use, offers one of the most flexible templating systems available with a lot of samples and a really helpful CSS comparison guide for styling emails for different email clients. Campaign Monitor distinguishes itself from Mail Chimp with its ability to resell distributions (you can set up campaigns for multiple clients through one account) and its payment structure.

Constant Contact

Price: uses pricing tiers based on the max number of emails you will send. Starts at $15 a month for up to 500 emails/month; there are also limits to the number of images you can host in your account.

Templates: Somewhat limited… the UI felt boxy, and although they support custom HTML/CSS, the pseudo code they used did not resemble anything I’d seen before: it uses XML-looking tags, so they are difficult to see once they are actually used alongside HTML. Clicking on the help links dumped out to a PDF page… but the PDF never loaded, so I had to email customer service before I could check out the Advanced Editor’s Guide and see how they handle templating.

Limits: varies per subscription level; the site itself is not friendly with Safari, and there were problems accessing their PDF docs.

API: Yes, but it’s a little hard to find: The interface is very XML centered (which is not PHP’s strong suit) and the docs are a bit hard to navigate and don’t include many examples. As expected, you can Import Outlook or csv files to populate your contact list.

Demo: 60 days with full support.

The strengths are that this company also does Online Surveys and Event Marketing in addition to the Email Marketing, and they get HIGH MARKS for customer service — a representative called me to follow up and sent me an email with links for everything I asked for. But I have to take off points for limiting special characters in the account passwords (If you’re gonna pretend like you care about my password strength by putting a Password Strength widget on your form, then you gotta follow through and allow special characters in the passwords!). Their representative assured me that their email templates were tested and functional across all browsers, but their control panel doesn’t work with Safari and there were problems accessing the PDF documents — even their little helpful survey widget asking “how you doing?” failed to submit properly, probably due to some browser-specific Javascript.

Mail Chimp

Price: HIGHEST MARKS! 3 options:

  • Monthly (50k subscribers): starts at $380, up to 600k sends.
  • Pay as You Go (you buy “email credits”, starting at 3 cents per email)
  • Forever Free (up to 3k sends/mo, list less than 500 contacts, emails include affiliate badge in footer)

no built-in surveys, but there is a built-in integration with SurveyGizmo.

API: Yes! HIGH MARKS! Documented for PHP, with some code samples in many languages. There are plugins for Drupal, FoxyCart, ExpressionEngine, etc.

Templates: HIGH MARKS! Yep, you can choose from their starter templates or use your own HTML/CSS using tags like this:
The tags even support if statements and formatting options. Template Language documentation is here:

Demo: Yep, free to try for as long as you’d like.

Summary: High marks all the way around. Flexible pricing options, custom templates, thorough API, and helpful videos. This is a very good product.


Price: starts at $49.95 for up to 1,000 names… but the pricing page is hard to find.

Templates: You can use your own HTML, CSS, and images, but you can’t store them on their server — it’s a bit difficult to see where to do this, exactly: go straight to the “Campaigns” menu and choose to use “No Template”. You can use up to 15 custom tokens (configurable under “Preferences”), e.g. ${token1}, but heavy use of tokens is discouraged because it increases database load.

API: Yes, it’s SOAP based, but (again) it’s hard to find: As expected, you can do bulk imports and bulk exports of contacts or audience data.

Demo: Limited 14 day “demo” is available, but it’s really an “opt-out” purchase requiring credit card authorization “If you do not cancel during your trial period, your account will remain active and be charged a minimum monthly fee of $49.95” as well as $0.01 per name in your database if you use more than your selected capacity.

Summary: Topica gets HIGH MARKS for customer service, and their biggest strength is that they’ve been around a long time and their servers are said to be in the “good-graces” of most ISPs and, like Constant Contact, they got people available to take your calls. They have reasonable customer service: questions were answered during normal times. However, the site is hard to navigate, it has some browser display issues and even a couple 404 errors in the control panel (!), and searches for documentation tended to funnel me back to contact their sales/support people instead of to actual pages. And personally, I’m not too keen on an opt-out purchase agreement in order to “demo” the software.


In summary, I’d say that MailChimp and Campaign Monitor were the best I looked at: both offered very flexible templates, thorough examples, and a flexible, well-documented API. I’d have to say that Campaign Monitor code samples are amongst the best I’ve seen, but Mail Chimp has excellent documentation and an equally flexible templating system. Topica came across as a bit boxy and overpriced for smaller campaigns given its features. Constant Contact was somewhere in the middle… I know it’s a popular option, but browser issues in the control panel and the lack of a sensibly-documented API make it an unattractive option for me.

Get EASEUS Data Recovery Wizard for Free (Giveaway)

EASEUS - Data Recovery Wizard Need to recover some important files? For a limited time, EASEUS is giving away a version of their Data Recovery Wizard. It ordinarily costs $70.

Giveaway Link

The Data Recovery Wizard is capable of recovering deleted files, but it can do much more. It can also help recover files on a disk partition after an accidental format, or even restore a lost partition.

Supported filesystems include FAT and NTFS, and supported operating systems include Windows 2000, XP, and Vista (32-64 bit). Unfortunately, it appears that Windows 7 is not officially supported.

Did you miss this giveaway, or want to try a comparable freeware program? Give Recuva a shot.

Writing Custom PHP Snippets for MODx (part VII)

This article and tutorial video takes on how to add custom PHP scripts (known as Snippets) to the MODx content management system. This covers MODx Evolution (version 1.0 and before), but many of these methods and principles are applicable to MODx Revolution (version 2.0) and PHP coding in general.

A video used to be embedded here but the service that it was hosted on has shut down.
Watch Video @ Blip.Tv

Adding a Snippet

MODx newcomers are sometimes confused as to where to upload the PHP files… YOU DON’T UPLOAD IT. You paste it into a database record. You can reference files on the file system, but you don’t have to.

1. To add a Snippet from the MODx (v1) manager go to Elements –> Manage Elements –> Snippets then paste in your PHP code.
2. Be sure to give it a unique name (I recommend avoiding spaces in the name)
3. Give it a category: this will make your Snippet easier to find in the manager.

Recommended Components of a PHP Snippet

This applies to ANY code you write, but for the record, please include the following documentation as comments in your Snippet:
1. SUMMARY: a sentence describing what the Snippet does.
2. INPUT: list any input variables the Snippet can accept. It’s good to note the data type (e.g. integer/string), whether or not they are required, or whether or not they have a default value.
3. OUTPUT: list any special output created by the Snippet. Usually it’ll just be HTML, but it’s good to note any external actions (e.g. whether it updates a database row).
4. EXAMPLE: give an example of how the Snippet should be called.

Sample Comment Block

Coding Suggestions and Rules of Thumb

1. Develop your Interface before you code: that bit about adding comments isn’t just for other users, it can help you determine how you want to be able to interact with your code. Coding to an interface is good way of establishing goals and structure before you even start writing the actual code.
2. Initialize your variables: This cuts down on the possibility of security exploits, bugs, and it makes your code easier to read, e.g.:
$output = '';
$garfield_characters_array = array();

3. Sanitize your input: if you are getting any user entered data (e.g. anything out of the $_POST or $_GET array), sanitize the data using regular expressions and if/then statements. Make SURE you have eliminated any possibility that the data is something other than what your program expects.
4. Test as you Go: PHP doesn’t have a built-in debugger, so don’t go too long without checking to see if your code still “compiles” (technically, you should check to see if it has a valid syntax and if it executes). Checking often will help you track down where you made a mistake.
5. Use Good Variable and Function Names: be descriptive. Don’t become a member of the hated “ASCII Preservation Society”. Besides, if you use unique variable names, it becomes MUCH easier to search for instances of that variable, and you’re less likely to have variable collisions.
6. Group your Code into Units: In a word, use functions that fit on the page. If you can SEE it, you’re less likely to UNDERSTAND IT. Chapters of uninterrupted code are hard to debug and test.
7. Reuse your Code: if there are cases in your code where you’re copying and pasting identical or NEARLY identical parts, then it’s time to relegate that code to its own separate function.
8. Log your Errors: if something goes wrong, let your users know about it. It’s a wonderful thing to use the MODx logging function.
9. DO NOT MIX HTML and PHP! There are a few cases where where this is acceptable, but it is good architectural design to separate your view (i.e. your HTML) from your logic. If you have to edit your PHP code to change div tags or CSS classes, then you probably did something wrong. Instead, use MODx Chunks to format Snippet output; your code will be MUCH cleaner as a result and MUCH easier to maintain.

Including Files from the File System

If you write anything more than simple Snippets, you’ll want to put your PHP file(s) on the file system and reference them from the Snippet stored in the MODx database. You can do this by including a standard include or require statement, e.g.


The standard MODx location would be in your own folder within the /assets/snippets directory.

Things to Remember When Including Files and Using Functions

1. Variable Scope: the $modx super-object and the methods that go along with it will not remain in scope within a funciton; use the global to ensure that the globally scoped $modx variable instance is used inside the function. Compare the two instances of the same API call:
// INSIDE a function
function inside_a_function($chunk_name,$garfield_characters_array)
global $modx;
$output = $modx->parseChunk($chunk_name, $garfield_characters_array, '[+', '+]');
return $output;

// Or OUTSIDE a function
$output = $modx->parseChunk($chunk_name, $garfield_characters_array, '[+', '+]');

2. You can’t return a value directly from an included file: because MODx treats Snippets as functions, it’s considered good form to always return a value, e.g. “return $output;” or “return TRUE;” but this MUST be returned from the original Snippet in the database; if you return output from the included file, you’ll have to return it again from the original database Snippet code. See the video for this quirk in action.
3. Take advantage of the File System: if you are developing stand-alone PHP files, you can use the bash terminal (on Linux or OS X machines) to test the PHP syntax. Simply navigate to the directory where your file is and type:
php -l name_of_your_file.php

Forgot your MODx Password? You can reset it…

I have *cough* never forgotten my password to anything because I followed Brian’s excellent advice about storing passwords, but just in case some of you have, I thought I’d help you out.

If you have access to your MySQL database, you can still log into the MODx manager.
If you have access to your MySQL database, you can still log into the MODx manager.

First off, if you are locked out of your MODx manager, you can use the standard link on the manager page to email you your password, but I’m outlining how to do this in the event that you either didn’t enter a valid email address or you aren’t receiving your emails somehow. There are two things I’m going to outline:

  1. Resetting your MODx manager password
  2. Adding a new MODx manager user

WARNING: Both options require that you have read/write access to the MySQL database where the MODx information is installed. PROCEED WITH CAUTION… THIS REQUIRES DATABASE EDITS.

Option 1 is nicely outlined by this article at Lucid Green: How to get into MODx when your blocked or lost your password. The article is a bit dated (2006), but it’s still valid… to summarize, do the following:

Resetting Your MODx Manager Password

We’re going to do two things here: change your password, and clear any blocks on your manager user.

  1. Login to your site’s database (e.g. using phpMyAdmin).
  2. Find the table modx_manager_users –in phpMyAdmin, find the table in the list on the left.
    phpMyAdmin lists all tables on the left-hand side
    phpMyAdmin lists all tables on the left-hand side

    NOTE: the modx_ is the default table prefix… your installation may use a different prefix, or it may use no prefix. If you honestly can’t locate the table, you may have to resort to the following query to find the table: SHOW TABLES LIKE '%manager_users';

    Can't find the table? SHOW TABLES LIKE ...
    Can't find the table? SHOW TABLES WHERE ...

    Type that at the MySQL prompt and any tables with a name ending in “manager_users” will be shown.

  3. Select your user from the list — in phpMyAdmin you can click the “Browse” tab to browse the rows in each table. Choose to CHANGE or EDIT the row.
    Edit your user
    Edit your user
  4. Select the MD5 function to operate on the password column. In phpMyAdmin, when you edit a record, you can use functions to operate on each column. Once you’ve selected to use the MD5 function, you can type your new password normally.
    Make sure you use the MD5 function!
    Make sure you use the MD5 function!

    If you are doing this via the MySQL command line, the actual query we execute looks something like this:
    UPDATE `your_db`.`modx_manager_users` SET `password` = MD5( 'changeme' ) WHERE `modx_manager_users`.`id` =1 LIMIT 1 ;

    NOTE: if you execute this query at the command line, you must type apostrophes to delineate your password (they will not be included as part of the password). If you are doing this via phpMyAdmin, do NOT type the apostrophes.

  5. Save the changes to the row.
  6. Please note the id for the row that you just edited… you may need it.
  7. Go to the modx_user_attributes table and find your manager user and edit this row (here’s where it’s handy to have that id from the above steps.
  8. Change only the following items then save the row:
    * Set “blockeduntil” to zero (0).
    * Set failedlogincount to zero (0).
    Remove blocks on your user
    Remove blocks on your user

    On the MySQL command line, your query looks something like this:
    UPDATE `your_db`.`modx_user_attributes` SET `blockeduntil` = '0',`failedlogincount` = '0' WHERE `modx_user_attributes`.`id` =1 LIMIT 1 ;
  9. You should now be done… head over to and login using your new password.

Adding a New MODx Manager User

This is a bit more devious, but the commands aren’t much different than the above, except that we are INSERTING rows into 2 tables instead of UPDATING them. There are many times I’ve needed to “let myself in” using this technique…

  1. As above, login to your site’s database (e.g. using phpMyAdmin).
  2. As above, find the table modx_manager_users –in phpMyAdmin, find the table in the list on the left. (If you have trouble finding the table, see the tip in step #2 above).
  3. Instead of updating an existing record, we are going to create one — in phpMyAdmin, click the “INSERT” tab. Be sure that you do the following:
    * type a valid username (usually, this is one word, lowercase)
    * type a valid password (use the MD5 function!)
    * Leave the id field blank (it will auto-increment).
    * Select “MD5” as the function for the password field (don’t forget!)
    * Leave the “Ignore” box checked — phpMyAdmin allows you to insert a couple rows at once, but we only need to insert one.
    Create a new User
    Create a new User

    Then click Go to insert the new record.

    The actual MySQL query used here is something like this:
    INSERT INTO `your_db`.`modx_manager_users` (
    `username` ,
    VALUES (
    'everett', MD5( 'changeme' )

  4. Remember the id of the newly inserted user! You’ll need it — phpMyAdmin shows it after the row was inserted. If you misplaced it, you can simply browse the table and find your user — remember the id!
    The new User's ID is shown here
    The new User's ID is shown here
  5. Go to the modx_user_attributes table and insert a new row:
    Add the following values:
    * id LEAVE BLANK. It will auto-increment.
    * internalKey should also be equal to the your user id from step 4.
    * role = 1 (for manager users)

    The actual query looks something like this:
    INSERT INTO `your_db`.`modx_user_attributes` (`internalKey` ,
    VALUES (
    '9', '1'

  6. That’s it. You should now be able to use that username and password to login into your MODx manager at