Author - Web Developer - Educator
Found 14 results for tag "php"
RSS Feed

My Journey into Git

Git logo

My Journey


My journey to discovering git was not an easy one. Since git was not covered in my formal programming lessons, I had to learn "version control" the hard way - the very hard way.

When I started learning HTML back in 2001, I learned to save and modify local files only. In 2002, I learned PHP and MySQL by working on a team server, but version control was not introduced (git was introduced to the world in 2005, but options like CVS or SVK weren't taught). My first local server came in 2004, when I learned to FTP files, instead of testing local files only.

I quickly updated to SSH'ing into the server and editing the direct code, and backuped the files using a combination of file extensions (such as .bak, date stamps like.20150515, and "Backup 20140523" folders).

...and I used this method up to about a year ago, when I took a git intro course online. Unfortunatly, I didn't quite understand the purpose of git because the demos kept talking about files that either already existed, or branches that were made by someone else - never really diving into the underlying purpose of git, which was version control.

I also couldn't get used to the idea of uploading my database login information (along with other private code) to the public site GitHub, which is great for open-sourced projects (see mine here), but not great for a company version control backup. So I dabbled in git for a while, until it hit me about a month ago: I could use git on my own server(s) and not have to deal with private repositories on GitHub (or the prices).

So, I started using git on my server for backups of projects site-wide. Then, I wanted to deal with errors on a non-production server basis (I didn't want the world to see my error testing, because that would be unprofessional). After getting a local Apache server, and MySQL server, and installing PHP (on Windows), I thought that I could use the GitHub software to create backups - but that was only for the GitHub site; I needed something to work with my own existing servers. Therefore, I installed Cygwin and got the Linux-enabled commands with git to my production server.

Overall, this is what I learned, and I hope it helps others:
The Process
The "direct" process vs the git process

Usage


(If you don't already have git set up on your remote server, then please do so by installing it - I recommend sudo apt-get install -y git. Otherwise, this won't work, and it's just a bunch of lines of code)
The SetupRemote (seperate from production folder):
mkdir [dir].git && cd $_
 
git init --bare
 
cp ~/post-receive.sample hooks/post-receive

The hooks folder deals with webhooks to automatically catch incoming files and do something with them (or at least, that's what I've discovered). To make the hooks run properly, you need a post-receive file, and this is what it should look like:
post-receive.sample
#!/bin/sh
 
GIT_WORK_TREE=[absolute path to production] git checkout -f


Once you have that set up, you can work on your local machine to create and modify files.
Local development machine
# If files do not exist
 
git clone [email protected]:[dir].git 
 
cd [dir]
 
# If files DO exist/update
 
cd [dir]
 
git pull
 

 
# Time for editing
 
vim [file]
 
[...]
 
[work on files, test on development machine]
 
[...]
 
(Ready to upload to production server)
 
git add [file(s) - * works as well]
 
git commit -m "Relevant message to update"
 
git push


One important note: As I have learned, your Git folder is not your production folder. I had my .git folder in my production folder of a project, and it was good for local editing, but not remote pulling. If your project folder (example: project.git) is somewhere else, like your home folder, then you can use the post-receive webhook to automatically pull committed files to the production area

Bonus: SSH Login w/o PW


Unless you want to type in your login information every time you git push, I recommend setting it up so your local machine/development server can automatically upload to the production server. If you are running a Linux system (and I recommend you do), then you can do the following:
SSH Password-less LoginOn your local machine (hopefully a Cygwin or Linux/Mac Terminal)
ssh keygen -t rsa
 
ssh [email protected] 'mkdir -p .ssh'
 
cat .ssh/id_rsa.pub | ssh [email protected] 'cat >> .ssh/authorized_keys'
B = Remote server



Summary


There is still quite a lot I don't understand about git (like branches, merging, etc), but I am getting better. I've been using this guide as a resource, as many other guides are very technical for a non-git person.

My takeaway: Better integration into team development instead of just "solo development"


Tags:#git #development #php #mysql #html #linux

RSS Feed

Update Youtube Video Privacy with API and OAuth

OAuthI posted this on my Facebook and Github, but I thought I'd post it here for a more public audience.

Recently, while working on Japanoblog, I realized that there was a problem: when we created videos, we would post them for our Patreon supporters 3 days early so they could get early access to them. However, I had a few options:
  1. I could post a separate video to Youtube/Vimeo/etc and share the link, but then wait 3 days and upload the real video and take down the temp one (which would mean 2x the work, and misleading stats)
  2. I could make the video "Unlisted" and share the link with our Patreon supporters, then wait 3 days and change the video to "Public" for all, or
  3. Do the 2nd option, but then have the server automatically change the status to "Public" when the video was ready to go public (as per the blog post publishing date

Well, being the pragmatic programmer that I am, I figured out how to make the server do it for me. But it was not an easy task (apparently, nobody had done this before - at least, on a public searchable point). After scouring the internet to dissect the Youtube API, Google OAuth (along with token creation), researching and pulling parts from about 7 different public user projects, and some patient testing, I finally have it...I hope.

This script is based on Dom Sammut's code and the Youtube Sample Code (PHP #1).

(Don't want to copy/paste? Here's the Github repository)

So, without further ado, here is what I have come up with:

First: get your tokens


You need to generate your tokens to get the process started.
<?php
 

 
#Primary code from https://www.domsammut.com/code/php-server-side-youtube-v3-oauth-api-video-upload-guide/
 
#Create Client ID and Client Secret by creating OAuth credentials 
 
# at https://console.developers.google.com/apis/credentials
 
# MAKE SURE YOU UPDATE YOUR REDIRECT URL TO MATCH!!!!!!!!!
 
$CLIENT_ID = "XXXXXXXXXXXXXX.apps.googleusercontent.com";
 
$CLIENT_SECRET = "XXXXXXXXXXX";
 
$application_name="APPLICATION_NAME";
 
 
 
// Call set_include_path() as needed to point to your client library.
 
#set_include_path($_SERVER['DOCUMENT_ROOT'] . '/directory/to/google/api/');
 
#Download the PHP Client Library from Google at https://developers.google.com/api-client-library/php/
 

 
#This has been installed using Composer - update if you download the files directly
 
set_include_path(get_include_path() . PATH_SEPARATOR . '/PATH/TO/vendor/google/apiclient/src/');
 
require_once 'Google/Client.php';
 
require_once 'Google/Service/YouTube.php';
 
session_start();
 
 
 
/*
 
 * You can acquire an OAuth 2.0 client ID and client secret from the
 
 * {{ Google Cloud Console }} <{{ https://cloud.google.com/console }}>
 
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 
 * <https://developers.google.com/youtube/v3/guides/authentication>
 
 * Please ensure that you have enabled the YouTube Data API for your project.
 
 */
 
$OAUTH2_CLIENT_ID = $CLIENT_ID;
 
$OAUTH2_CLIENT_SECRET = $CLIENT_SECRET;
 
#$REDIRECT = 'http://localhost/oauth2callback.php';
 
$REDIRECT = 'http://YOUR_URL.com/oauth2callback.php';
 
$APPNAME = $application_name;
 
 
 
 
 
$client = new Google_Client();
 
$client->setClientId($OAUTH2_CLIENT_ID);
 
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
 
$client->setScopes('https://www.googleapis.com/auth/youtube');
 
$client->setRedirectUri($REDIRECT);
 
$client->setApplicationName($APPNAME);
 
$client->setAccessType('offline');
 
 
 
 
 
// Define an object that will be used to make all API requests.
 
$youtube = new Google_Service_YouTube($client);
 
 
 
if (isset($_GET['code'])) {
 
    if (strval($_SESSION['state']) !== strval($_GET['state'])) {
 
        die('The session state did not match.');
 
    }
 
 
 
    $client->authenticate($_GET['code']);
 
    $_SESSION['token'] = $client->getAccessToken();
 
 
 
}
 
 
 
if (isset($_SESSION['token'])) {
 
    $client->setAccessToken($_SESSION['token']);
 
    echo '<code>' . $_SESSION['token'] . '</code>';
 
}
 
 
 
// Check to ensure that the access token was successfully acquired.
 
if ($client->getAccessToken()) {
 
    try {
 
        // Call the channels.list method to retrieve information about the
 
        // currently authenticated user's channel.
 
        $channelsResponse = $youtube->channels->listChannels('contentDetails', array(
 
            'mine' => 'true',
 
        ));
 
 
 
        $htmlBody = '';
 
        foreach ($channelsResponse['items'] as $channel) {
 
            // Extract the unique playlist ID that identifies the list of videos
 
            // uploaded to the channel, and then call the playlistItems.list method
 
            // to retrieve that list.
 
            $uploadsListId = $channel['contentDetails']['relatedPlaylists']['uploads'];
 
 
 
            $playlistItemsResponse = $youtube->playlistItems->listPlaylistItems('snippet', array(
 
                'playlistId' => $uploadsListId,
 
                'maxResults' => 50
 
            ));
 
 
 
            $htmlBody .= "<h3>Videos in list $uploadsListId</h3><ul>";
 
            foreach ($playlistItemsResponse['items'] as $playlistItem) {
 
                $htmlBody .= sprintf('<li>%s (%s)</li>', $playlistItem['snippet']['title'],
 
                    $playlistItem['snippet']['resourceId']['videoId']);
 
            }
 
            $htmlBody .= '</ul>';
 
        }
 
    } catch (Google_ServiceException $e) {
 
        $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
 
            htmlspecialchars($e->getMessage()));
 
    } catch (Google_Exception $e) {
 
        $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
 
            htmlspecialchars($e->getMessage()));
 
    }
 
 
 
    $_SESSION['token'] = $client->getAccessToken();
 
} else {
 
    $state = mt_rand();
 
    $client->setState($state);
 
    $_SESSION['state'] = $state;
 
 
 
    $authUrl = $client->createAuthUrl();
 
    $htmlBody = <<<END
 
  <h3>Authorization Required</h3>
 
  <p>You need to <a href="$authUrl">authorise access</a> before proceeding.<p>
 
END;
 
}
 
?>
 
 
 
<!doctype html>
 
<html>
 
<head>
 
    <title>My Uploads</title>
 
</head>
 
<body>
 
<?php echo $htmlBody?>
 
</body>
 
</html>


Now that that's all set, save the response to a file (I recommend "the_key.txt"), and modify and run the following:
<?php
 
/**
 
 * This code is to be run automatically to update a Youtube video's privacy status
 
 *
 
 * First, generate your key using "get-token.php" - read the notes below for generation
 
 * Next, update this file with the appropriate information (path to key file, Client ID, 
 
 *    Client Secret (OAuth Required), Application Name, Database Login, Database Query, and
 
 *    location of PHP Client Library - all download information is below)
 
 * 
 
 * @author Kyle Perkins
 
 * @site https://github.com/kode29/google-youtube-api-privacystatus
 
 * 
 
 * NOTICE: Rest of copyright should be in tact for other scripts (Dom Sammut (domsammut.com) and Ibrahim Ulukaya (Google)
 
 * Last Update: 20160108
 
**/
 

 
#Primary code from https://www.domsammut.com/code/php-server-side-youtube-v3-oauth-api-video-upload-guide/
 
# Mixed with sample code from https://developers.google.com/youtube/v3/docs/videos/update (PHP #1)
 

 

 
#Generate the "the_key" with get-token.php and store it into "the_key.txt" or wherever you want to store it BEFORE running this script.
 
# Also, make sure "the_key" has a REFRESH TOKEN!
 
$key_file = "/path/to/the_key.txt";
 

 
#Create Client ID and Client Secret by creating OAuth credentials 
 
# at https://console.developers.google.com/apis/credentials
 
# MAKE SURE YOU UPDATE YOUR REDIRECT URL TO MATCH!!!!!!!!!
 
$CLIENT_ID = "XXXXXXXXXXXXXX.apps.googleusercontent.com";
 
$CLIENT_SECRET = "XXXXXXXXXXX";
 
$application_name="APPLICATION-NAME";
 

 
#CHeck the DB for updated videos
 
$video_list=array();
 
    $dbh = new PDO('mysql:host=localhost;dbname=DATABASE_NAME', "DATABASE_USER", "DATABASE_PW");
 

 
	$sql="select `video` from `TABLE` where `stamp` like '".date("Y-m-d H:i:")."%'";
 
				$query = $dbh -> prepare($sql);
 
				$query->execute();
 
				if ($query->rowCount() > 0){ #rowCount() won't work on some databases
 
					$values = $query->fetch(PDO::FETCH_ASSOC);
 
					while (list($key, $value) = each($values)){
 
						$video_list[]=$value;
 
					}
 
				}
 
$key = file_get_contents($key_file);
 
if (count($video_list)>0){
 
foreach($video_list as $VIDEO_ID){
 
	$VIDEO_ID = str_replace("https://youtube.com/watch?v=", "", $VIDEO_ID);
 
	$VIDEO_ID = str_replace("https://youtu.be/", "", $VIDEO_ID);
 

 
#Sample $VIDEO_ID can be "gYY3fVz6PjY";
 
/**
 
 * This sample adds new tags to a YouTube video by:
 
 *
 
 * 1. Retrieving the video resource by calling the "youtube.videos.list" method
 
 *    and setting the "id" parameter
 
 * 2. Appending new tags to the video resource's snippet.tags[] list
 
 * 3. Updating the video resource by calling the youtube.videos.update method.
 
 *
 
 * @author Ibrahim Ulukaya
 
*/
 

 
// Call set_include_path() as needed to point to your client library.
 
#Download the PHP Client Library from Google at https://developers.google.com/api-client-library/php/
 

 
#This has been installed using Composer - update if you download the files directly
 
set_include_path(get_include_path() . PATH_SEPARATOR . '/PATH/TO/vendor/google/apiclient/src/');
 
    
 
require_once 'Google/Client.php';
 
require_once 'Google/Service/YouTube.php';
 
session_start();
 

 
/*
 
 * You can acquire an OAuth 2.0 client ID and client secret from the
 
 * Google Developers Console <https://console.developers.google.com/>
 
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 
 * <https://developers.google.com/youtube/v3/guides/authentication>
 
 * Please ensure that you have enabled the YouTube Data API for your project.
 
 */
 
$OAUTH2_CLIENT_ID = $CLIENT_ID;
 
$OAUTH2_CLIENT_SECRET = $CLIENT_SECRET;
 

 
$client = new Google_Client();
 
$client->setClientId($OAUTH2_CLIENT_ID);
 
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
 
$client->setScopes('https://www.googleapis.com/auth/youtube');
 

 
#$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'], FILTER_SANITIZE_URL);
 
# If running via Cron, HTTP_HOST may be blank
 
$redirect = filter_var('http://YOUR_URL/' . $_SERVER['PHP_SELF'], FILTER_SANITIZE_URL);
 
$client->setRedirectUri($redirect);
 

 
$scope=array("https://www.googleapis.com/auth/youtube", "https://www.googleapis.com/auth/youtubepartner", "https://www.googleapis.com/auth/youtube.forcessl");
 

 
// Define an object that will be used to make all API requests.
 

 

 
#if (isset($_GET['code'])) {
 
#  if (strval($_SESSION['state']) !== strval($_GET['state'])) {
 
#    die('The session state did not match.');
 
#  }
 
#
 
#  $client->authenticate($_GET['code']);
 
#  $_SESSION['token'] = $client->getAccessToken();
 
#  header('Location: ' . $redirect);
 
#}
 
#
 
#if (isset($_SESSION['token'])) {
 
#  $client->setAccessToken($_SESSION['token']);
 
#}
 
$client_id = $CLIENT_ID;
 
$client_secret = $CLIENT_SECRET;
 
#var_dump($key);
 

 
  $client = new Google_Client();
 
    $client->setApplicationName($application_name);
 
    $client->setClientId($client_id);
 
    $client->setAccessType('offline');
 
    $client->setAccessToken($key);
 
    $client->setScopes($scope);
 
    $client->setClientSecret($client_secret);
 

 
// Check to ensure that the access token was successfully acquired.
 
if ($client->getAccessToken()) {
 
/**
 
         * Check to see if our access token has expired. If so, get a new one and save it to file for future use.
 
         */
 
        if($client->isAccessTokenExpired()) {
 
            $newToken = json_decode($client->getAccessToken());
 
            $client->refreshToken($newToken->refresh_token);
 
		#This is for debugging if your token is not regenerated
 
	    #var_dump($client->getAccessToken());
 
            file_put_contents($key_file, $client->getAccessToken());
 
        }
 

 
$youtube = new Google_Service_YouTube($client);
 

 
  try{
 

 
    // REPLACE this value with the video ID of the video being updated.
 
    $videoId = $VIDEO_ID;
 

 
    // Call the API's videos.list method to retrieve the video resource.
 
    $listResponse = $youtube->videos->listVideos("status", array('id'=>$videoId));
 

 
#	array( 'id' => $VIDEO_ID, 'status' => array('privacyStatus' => 'public')));
 

 
    // If $listResponse is empty, the specified video was not found.
 
    if (empty($listResponse)) {
 
      $htmlBody .= sprintf('<h3>Can't find a video with video id: %s</h3>', $videoId);
 
    } else {
 
      // Since the request specified a video ID, the response only
 
      // contains one video resource.
 
      $video = $listResponse[0];
 
	$videoStatus = $video['status'];
 
	$videoStatus->privacyStatus = 'public'; #privacyStatus options are public, private, and unlisted
 
	$video->setStatus($videoStatus);
 
	$updateResponse = $youtube->videos->update('status', $video);
 

 

 
#    $htmlBody .= "<h3>Video Updated</h3><ul>";
 
#    $htmlBody .= sprintf('<li>Tags "%s" and "%s" added for video %s (%s) </li>',
 
#        array_pop($responseTags), array_pop($responseTags),
 
#        $videoId, $video['snippet']['title']);
 
#    $htmlBody .= '</ul>';
 
$htmlBody = "We're Good!"; #Just a debug phrase to know that the script completed successfully. Not required to output
 

 
  }
 
    } catch (Google_Service_Exception $e) {
 
      $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
 
          htmlspecialchars($e->getMessage()));
 
    } catch (Google_Exception $e) {
 
      $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
 
          htmlspecialchars($e->getMessage()));
 
    }
 

 
    $_SESSION['token'] = $client->getAccessToken();
 
    } else {
 
      // If the user hasn't authorized the app, initiate the OAuth flow
 
      $state = mt_rand();
 
      $client->setState($state);
 
      $_SESSION['state'] = $state;
 

 
      $authUrl = $client->createAuthUrl();
 
      $htmlBody = <<<END
 
  <h3>Authorization Required</h3>
 
  <p>You need to <a href="$authUrl">authorize access</a> before proceeding.<p>
 
END;
 
    }
 
#      echo "<body>$htmlBody</body>";
 
}}
 
	?>
 

Again, Here's the Github repository)


Tags:#php #mysql #japanoblog #video #youtube #api #oauth

RSS Feed

What I learned from my first Startup Failure

I was inspired to write this from many articles referenced in the December 21st Edition of Startup Digest. In that newsletter, I found many articles about "What I learned from my Startup Failure" (or along the lines). Therefore, I thought I would write this.

Work on the business, not in the business


As the quote from Michael Gerber goes, it is best to work on the business instead of in the business. In my college career as an aspiring Entrepreneur, I never really understood that quote. My logic consisted of the following phrase: "If you can do the task, then do it and market yourself. You are the business - it's your idea, it should be your fame." It took me roughly 7 years to understand that I was wrong.

From Day 1 up until the end of 2012, I was the "CEO/President" of Shadow Development (CEO from 2005-2009ish, when I learned that if I was a "CEO of a small company, then the credibility hasn't been built", so I downgraded to "President" thinking "Yeah, that'll work"; yeah right). In 2013, I stepped down from "President" to "VP of Operations" basically stepping aside from the Presidential duties and taking a look at the business as an outsider and not just as myself.

That was probably the best thing that I've ever done. Not in the "This was a great decision, it made a million bucks" kind of way, more of a "I see what is actually happening and not just want I want to happen."

I saw that the business had been running on my hopes, my dreams, and my stubbornness for far too long. I should have shut the doors a long time ago if I saw this happening. Even throughout the ups and downs, it was my stubbornness that kept my business up for so long.

What should I have done? Hired someone to take care of the project management, project development, and selling. I shouldn't have done those things back when the business was started. I should have managed the business, sought out investment, asked others for help, evaluate the cash flow, find ways to save money, establish marketing channels, etc; not become the primary developer.

Basically, I've learned that "If a person can't do the work, then they should be fired. Based on the logic, if you can't do the work, then fire yourself, but then the company will no longer exist." However, I couldn't fire myself because I was too damn proud.
Entrepreneur Fail


Surround yourself with like-minded people who can do the jobs you ask


After dealing with managing people, it's a lot harder than just "Do this task." You must take into account their schedule, their emotions, their skill sets, not "playing favorites", their quality, etc. I always thought that "If I surround myself with people that have the skill sets, I can delegate tasks to them" (see the next point as well). Well, I hired developers, designers, project managers, sales people, and interns. What did I get: next to nothing.

My designers didn't have anywhere near the skill sets that I had imagined. I thought "They have design experience and an eye for design. Their designs will be great!" What I received was "This is what I think will work", not what I had in mind.

My developers knew what they were doing (for their development levels), but they had a long way to go in order to provide what we promised the clients.

My sales people said they knew the material, they knew the sales process, they knew the contracts and services. However, they also fell short of expectation and probably couldn't sell water to a dying man in the desert. (Hell, neither can I)

My interns were treated as "gofers" instead of the "learn as you work" kind of experience. I didn't like that.

My project manager was great at managing projects, but we didn't have many projects to give them that weren't already being managed (or the budget to allocate to them).

In short: Just because the person you hired had the title "X" doesn't mean that they can do what you expected of your last "X".

(However,) Hire Slow and Fire Fast


Just because you are in a company doesn't mean you need to have 10 employees around you. Take your time to hire the right person for the right job. If you hire someone just because they have "X" in their previous title, that doesn't exactly mean that they know everything there is about X (for example: just because someone says they are a salesperson doesn't mean they can actually sell).

If a person isn't the right fit for a job, get rid of them. If a person isn't doing what you want them to do, get rid of them. If the person is trying to take over the company, get rid of them - quickly.

I know that many Managerial books have said "Hire Slow, Fire Fast", and I never really understood what that meant. However, now I know what they meant, and I have been burned in order to learn that.

Don't think that "If I just keep them on the payroll for another few days/weeks/months, things will turn around." That never worked for me. I also knew that my stubbornness got in the way for "being nice to people" because I wanted to see them come around, like in TV shows. Yeah right.

If a person isn't doing their job, you have 2 choices, and you better choose one fast. Either 1) Train them to do their job better, or 2) Fire them without resentment.

If I could go back in time and do one or the other, I would have chosen #2 in a heartbeat instead of waiting around for about a year to see what would happen. I would have rather hired the right person for the job, instead of someone who I thought would work based on their previous title.

Never expect to copy yourself


If you are super-skilled in X, don't expect others to be as well. They may have the same basis, but you can't expect them to know what you know in 24 hours - especially if you were trained over several years.

I have been studying Web Development since 2001, since my first HTML class in high school. Since then, everything else has been self taught: Advanced CSS, PHP, MySQL, jQuery, XML/XSL, DHTML, JavaScript, Python, Ruby on Rails, etc. So I've had over 10 years of experience in the field.

When I hired on extra developers, I had a high expectation: they were on the same level as myself and could crank out stuff just like me.

I could not have been more wrong.

Most of the developers I hired on worked under the premise that they would learn additional skills under my leadership. Well, I spent roughly 2 months teaching them the most common advanced PHP techniques, jQuery items, CSS3 markup, and even Database management with MySQL (as much as I could do). What did I expect? I expected them to come out of the training doing what I was doing. The actual result: I advanced their skill set a bit, but they were still a long way off from developing what we needed for our clients.

I always said "If I could copy myself, I would", but in today's technological era, that's an impossibility (unless you're a sheep).

Basically, it came down to the education gap between me and the other developers; I knew a lot more than they did, and they needed to know a lot more if they were to crank out stuff like me. That wasn't going to happen in 2 months or 2 years - they needed the "on-the-job training" like I had; they needed the "decade of wrong-doings" that I had; they needed to "do things the hard way before learning the ease of a shortcut" like I did (I spent nearly 2 months of nights and weekends in my room hand-coding a visual editor (without advanced PHP functions) because I "wanted to do it").

Location, Location, Location


Waynesville, NC, is not the place for a high-tech kind of company. It took me many years of trial and error (mostly error) to figure that out. As my business advisor once said, "I'm sorry you are in this area." If I was in Raleigh, Charlotte, Atlanta, San Francisco, New York, etc, it would make more sense, but you can't force a business model (or technique) into an area that isn't ready for it. Most of the businesses in this area are "Mom and Pop" stores with owners who are usually the ones behind the counter. While there isn't a problem with this kind of business operation, it isn't the kind of market that can allocate thousands of dollars towards marketing.

I also didn't find out until a few years in (after moving to a small office space in 2007) that we could not put up any additional advertising on the building we were renting, or near the building in general. The landlord did provide us with a large sign on the front that had all of the businesses listed that were inside our building, but any additional logos, signs, even something saying "HERE WE ARE" were prohibited.

My suggestions: prime location for exposure (like Main Street), a professional environment that you can advertise with/on, and/or an easy access office.

Price is not always a factor, but it is a big one


Price may be beneficial, but you can always set it too high or too low. Most people in this area want a low-cost solution, and if your only price range starts in the $1000's, you can easily price yourself out of a market, which is pretty much what we did. After calculating how many hours went into the research, the design testing, the development creation, the usability tweaking, and the launching of a site, $1000 was the bare minimum for the kind of work we produced. While a company in Atlanta wouldn't bat an eye at that, people up here would turn us away within a second. The people up here want the $5 solution, the "What can I pay you to do that won't cost me an arm and a leg" type of solution, the "I have $100, what can you do" kind of solution. Not the "$1000 will get you X, Y, and Z" kind of solution.

Create an enjoyable work environment


A happy team is a productive team. While I'm always the one to think of an enjoyable environment such as Google, IDEO, Patagonia, etc, we didn't really have that, and it showed. Although the extent of our budget allowed for the purchase of some darts with my own dart board, it didn't provide much of an "enjoyable environment" as it were. I also brought in my personal PS2, games, and TV to hopefully provide a few hours of entertainment for the staff. It was never touched. I would love to provide a ping-pong table, a pool table, a HDTV with PS3 entertainment, and more, but our budget didn't allow for it, because I didn't put the money aside to do so.

Don't grow too fast


Grow your company as needed. Don't expect to have your own glass-covered office in 5 years. Don't expect to have a pool table and 200" TV next year. Don't expect to have 30 employees and your own stock options next month.

If you grow too fast, your goals and focus will be thrown out of alignment and you will be focused on the end-goal, not the "right now" goal. So your company is 5 years old - does that mean you need your own conference room? No - invest that money into more marketing and product development, maybe even some staff training. If you absolutely need that conference room, then your company will tell you so. Until then, find a library meeting room, somebody's house, or even a coffee shop to meet at.

Don't get caught up in meetings


Just because you have meetings doesn't mean that they (or the people who attend them) will be productive. As I learned from my years at Last Minute Productions, the majority of items that were discussed in the board room usually stayed in the board room. During my reign as President, I vowed to change that, although my board didn't like the idea (they wanted more money for doing less). In most Board Meetings, assigning a task to someone that will get it done usually results in responsibility, accountability, reliance. If a task is not assigned, then it will not get done.

Also, I always thought it was a great idea to bring my board member attendees up to speed with what has been happening. Usually, that turned into 45 minutes of me blabbing about calls, meetings, and other items that didn't concern, relate, or include the other members. I could have just as easily summarized those blabbings, or even just said the end result and cut the time down to 5 minutes, but my stubbornness got in the way and I was too proud of myself for all of the work that I did (and nobody else). What I should have done is delegated out some of those items to the members so I wasn't always bogged down with items like that.

Don't over complicate things


When I first started my company, I thought that the idea of making websites easy to use would be easy. I mean, the whole world was doing it wrong: Geocities, Yahoo Sites, Homestead websites, even "Welcome! I'm HTML Code!" websites were designing things wrong. The wrong resolution, relying on "Best Viewed in Internet Explorer" tags, over complicating things with animated GIF's to make the site "cute", creating 5-minute flash "splash pages" that you had to sit through in order to get to the site's content, and the list goes on.

I thought I could change this. Instead, I made it more complicated.

In The Oatmeal's blog posting on "How A Web Design Can Go Straight To Hell", the artist/programmer/fun guy Matt Inman talks about exactly what I mean: taking a poorly designed website from the 90's and improving it with high-def graphics, a nice content flow, a beautifully laid out menu, and easy-to-read items. Instead, the client wants what they had: a poorly designed site from the 90's because that's what they had, and that's what they're used to (hence, the "cute kitty", the "mother who designed a Bake Sale flyer in the 80's", and the "pop" and "edgy" items that only exist in their mind). Personally, I also hated it when people would come into Staples (when I worked there) and acted like they knew more than I did in Technology - the same concept applies here: when clients think they know what they should based on the latest article, blog, or news report and want "Web 2.0 items" and "SEO Techniques" integrated into their sites, thinking that those are the most important and absolutely necessary items to have a great site.

I also tried to put too much focus on "how the site worked" and not so much on "how the site looked". My logic said that "if the site worked beautifully, we can design around it. Too many companies focus on how the site looks and not how it operates. Let's flip that." I was wrong. I knew that people went to websites because they looked great (the latest graphics, the flowing sections, even the effects on the photos). I also knew that people left websites because they operated poorly (bad links, page redirection went wrong, server errors that didn't make sense, etc). I wanted to focus on the development of the site to make it operate as expected.

What should I have done? Balanced the design and development, making sure that the design was exciting and the site operated as expected - nothing more.

Always have a backup. And when you do, backup some more


Having your hosting company hold your content hostage is bad business. Over the course of 8 years, I switched hosting companies 4 times. In 2005, I self-hosted, thinking that it wasn't that hard. I was wrong.

In 2006, I moved over to "Website Source" and it was pretty easy to manage, but it cost roughly $66/mth. I did lose some data while with them, and it cost me $50 to recover it from one of their backups.

I migrated to some larger systems while with Website Source, but in 2013, I had to call it quits. In early 2013, I switched to another company (I won't name names here) that would provide me with direct access to our server with backups.

In late 2013, they called it quits with us and froze our server, which meant that all of our data (ours, customers, databases, financial, emails, etc) was gone, and it took me nearly 4 days to get them to "unfreeze" the server so I could transfer the data off. And their "promise" of a backup system? Never happened, so the only place that our data existed was on one single hard drive.

Late 2013-now, I'm now back on my own hosted server (with a lot more experience for managing) and have a backup script to an external server running 2x/day, just in case.

Overall, backup all of your data. Once you do, back it up again, because you never know when you will need it.

If you are going for professional, don't cut corners


Even the iPhone 4 launch was pushed back due to some flaws. Back in 2005, our slogan was "We stay in the shadows, you get all the credit" which was okay. In 2007, our slogan changed to "Simply Professional, Professionally Simple." Personally, I liked it; it portrayed an image of professionalism along with the confidence of knowing that we would be simple (not to contradict a previous point, but that wasn't relevant right now). We even got some professionally printed white polo's that had our company logo on them to display our professionalism. Whenever I went to a client meeting, I made sure that I was clean shaven, my hair was washed, my teeth were brushed, my polo was clean and tucked, my pants were clean, and my shoes were shiny, along with other items to show that I was "A Professional." I thought that this image would be a standard and everybody who copied me would show that we were professionals.

My primary sales person thought otherwise. When I first brought on our primary sales person, I knew that he was a laid-back guy. He was my old freshmen english professor, and I thought that with his "relaxed state" and our "hardened professional mind", we could go far. I didn't expect him to cut so many corners in our "professional environment." For starters, he thought that just because we were a "company", we had millions of dollars sitting in the bank, and $20 meal wouldn't be missed. Little did he know that the $20 he just spent on a meal could have gone towards other resources, such as supplies (and we didn't have much in the bank to begin with, so $20 could have been all that we had!) To make matters worse, I had to order a button up shirt instead of a polo for him, because he didn't like things over his head. When he went to client meetings, he rolled up the long sleeves on his button up shirt. Sometimes he arrived with his button up shirt untucked from his pants, and even left it unbuttoned. He would approach potential clients like they were old friends, taking the conversation on completely irrelevant tangents, and even throwing in a few curse words to get his point across (playfully). Although I knew that we should have let him go a lot earlier, my stubbornness got in the way and thought "he can be reformed." Again, I was wrong...for 2 years (he finally left under his own accord).

Keep in touch with your team


Miscommunication (or lack of communication) can make team members drift apart, and if you constantly rely on them, then losing them is not an option. When I used to talk to my team members on a daily basis, it almost got annoying. However, going days, weeks, even months without any communication seemed like we were drifting apart, almost to the point of "Do I have a job anymore?" (which I have received a few times). Communication is key, but effective and frequent communication is better.

Make all of your goals and intentions clear


Stick to a timeline, schedule your goals and expectations, make sure that everyone knows what the result will be. Missing a deadline is a lot more than just a lowered grade on a test - in the real world, it can mean losing somebody's business.

Whenever I assigned a task to someone, I expected it to get done by the deadline set. When the person says "no problem", my expectation of getting the job done seems worthy. However, when the deadline comes along and there isn't a completed project (or even progress on said project), then my reliance becomes less and less, almost to the point of "If you can't do the task, then you're fired." But, once again, my stubbornness got in the way and I didn't fire anybody. However, when something is delegated out to a person, and that person hasn't completed the task, then the blame is put on the person that delegated the task in the first place, and personally, I'm tired of being blamed for delegating uncompleted tasks (which is why I would take the tasks in the first place, so that I knew it would get done on time, but then I was blamed for not giving anybody else any tasks and hogging everything). It's an endless cycle....

Basically, make the goals and intentions clear. Make the deadlines important, and the punishments clear. Don't allow anyone to slip by because of something that isn't understood.

Never stop seeking out investments


Bootstrapping is a great solution...if you are in your 2nd week of operations. But much later, you will need to seek out more investments in order to grow your business and can't always rely on bootstrapping. If you are taking in your income and only keeping 30% of it as profit, then you won't have enough to cover any additional items, such as moral-boosting get-togethers, upgraded office equipment, RENT, and more.
Also, make sure your team can get paid. Basically working a "freelance" job (or an on-call part-time job) isn't going to pay the bills. If your team isn't committed to you, they will seek out other opportunities for income and leave you.

Set the example


You are the boss. Show your team what you can do. Otherwise, you're just a team member. If you need to resolve a conflict, then resolve it peacefully. If you ARE the conflict, fire yourself or get a 3rd party arbitration.

If you expect your staff to be there on the holiday, you need to show up on the holiday(s). If you expect your team to be there at 7am, then make sure that you are there at 7am (preferably with a hot pot of coffee). If you expect your team to work 40 hours on a project, then you work 40 hours on a project.

Don't just sit back and expect to take the credit while your team does all of the dirty work.

Never expect someone to completely understand what you are offering, and the benefit of it, within the first few minutes of your presentation


Entrepreneur Fail

I expected people to see what we offered and go "Hey! That's what I need! And look at all of the great features!". Instead, I received glassy-eyed stares, basic technology questions, bored attendees, and other signs saying "I have no idea what you are talking about."

When we offered "Advanced, modern websites with the technologies of HTML, CSS, PHP, MySQL, jQuery, and more", I expected a bit of confusion among the crowd, especially at the acronym part, but that was to show our expertise in the technologies and show that "We know what we are doing." Therefore, the potential client would say "They know what they are doing - we should go with them." Instead, it resulted in "I have no idea what that means; therefore, I'm not listening to you anymore."

My expectation was that people would easily understand what we were offering by our presentations, our elevator pitches, and our portfolio. Instead, most people didn't understand or care what I had to say. It basically took an educational session to bring them up to the same level as us for them to understand what we were saying. Let's just say that it didn't turn out well.

Therefore, simplify your techniques. Don't throw in all of the bells and whistles just to impress people. Tell them what they want to know, not what they need to know. It's like an interview: tell the person interviewing you what they want to hear ("I can do the job"), not what they need to here ("With my three decades of experience in X, I suggest that you should do A, B, and C"). Even if it means that they are missing out on an important aspect (ie: security flaws), they want to hear what they want to hear.

Do research on your market, then create the product - not the other way around


I always thought "If you build it, they will come" (insert movie reference here). That may work for some items, but not everything, such as my business. I created a "modern, high-tech web design company" that I thought people would be busting my door down for us to work with them. Instead, of the clients that we found in the Western North Carolina area, most of them were busting my door down because they were unhappy with the service.

I also thought that if we created a service that cranked out quality websites and charged a monthly fee to maintain them, then we would be rolling in the profits. Easy as pie, right? Well, it wasn't so. First, we had to go out and almost physically drag the potential clients to sign with us. Then we had to make sure that we did almost every project underbudget, then provide continuous tech support for the most basic items, even if it went against our "best practice" recommendations. Yada yada yada.... Not really the best idea for starting a business, then sitting back and relaxing while the profits roll in.

Another developer wrote an article about a similar concept, and I agree with him: Research the market, then build the product. Just because you have the best designed, flashy, sparkly billboard in the desert doesn't mean that people will see it, or want to see it.

Don't screw up your credit


As an 18-year old, my first credit card was a surprise. I just entered college and had a personal banking account with Wachovia for a few months (there wasn't a branch of my local bank in my college town, so I switched). A few months in, I received a card from Wachovia saying "Your New Credit Card". I thought "My parents warned me about this. My sister had a bad experience with one. I should not use this unless it is an absolute emergency." So what did I do? I went to Taco Bell and got a drink to test the card out. It worked!

A month later, I used the card some more on a few small things, thinking "I'm getting paid, I can pay it off." I receive my first credit statement. "Amount due: $105. Minimum payment required: $5." When I saw this, I thought "Great! I can make the minimum payment and pay it off slowly." I would like to point out that this card had a limit of $10,000 because I used Debit since I opened the account.

Six months go by. I pay the minimum payment for six months. My credit bill is probably in the medium-to-high 100's. Suddenly, on my credit bill for the 7th month, minimum payment jumps to near $75 or so. In my mind, I'm thinking "Whoa! What happened? This is insane!" I call Wachovia asking about the jump in payment, and they say it's a "Promotional Period that has expired." I told them that I never heard of a promotional period when this started, and I was never offered it as the card was sent to me in the mail, which I never signed up for anyway. The lady was extremely persistent on the fact that since I had the card and was using it, that I was liable for any purchases, regardless of what the promotional period was or what the minimum payment was. I told her "I can't make the minimum payment right now! I don't even have a (well funded) job!" She basically said something along the lines of "Not my problem."

I will say at this point: I never expected my personal credit to cross paths with my investment record.

A few years go by, I get a job that pays roughly $150/mth. Not bad for a part-time gig. I start to pay off my credit bill little by little. Next thing I know, my minimum payment jumps to around $250/mth. I call again and complain, but I was told again by the bank "Not [their] problem". So, my credit bill keeps going up because I can't make the minimum payment, and 29% interest is gained on the bill because it hasn't been paid. At the $7,000 credit mark, I completely stopped using the card altogether. After all of the interest and "late fees" added up, my card topped the $10,000 limit within no time at all. I told Wachovia "I don't use the card. I will never use the card again. Please close the account and I will pay it off." They basically said "The account can't be closed until the card is paid off." So, the account stayed open and accrued interest. Just great. It finally got closed a few years ago, and I'm still attempting to pay it off, but I expect that will take about 10 years or so to pay it all off.

Later in my college career, I start to seek out additional funding for my business. The first few places I go say "Your business plan looks good, but in order to get you funding, we need to take a look at the Owner's credit report." What? This was never covered (in depth) in college!

Let's just say that pretty much every investor I talked to said "Yes" to the business, the plan, and everything else, then changed their mind to "No" once they saw my credit score.

I have never accepted another credit card since the first one, and never will again, regardless of any "offers", "Cash back deals", or "frequent flier points" they provide.

If I could go back and change history, I would have never bought that drink at Taco Bell and shredded the credit card. My credit score would keep going up because I was making so many on-time payments with debit, and I could probably have a few thousand (if not hundred thousand) in investments right about now.


Summary
I know that I've covered a lot of points in this entry. I just wanted to get most of them off my chest before I went to some type of therapy (Even referencing this article was a stretch). Hopefully, other entrepreneurs will read this and discover that not everything will work out like in the movies, the books, or even in the classroom scenarios. You are not indestructible. Your life does hold secrets that others will find. You cannot hide from everything. There is no "reset" button. And, you should fail before you succeed. I know many Venture Capitalists may not look at me because "I have a failed business", but you know what? So what. I've gone through 8 years of self-torment, stubbornness, hopefulness, and grief to know what to do and what not to do. If that means that you won't look at me because of that, then I will go elsewhere.

I'm not saying that you (as an entrepreneur) should read every business book out there. I'm not saying that every article on the internet is right. Go through your own experiences and learn the mistakes. Even Albert Einstein once said, "Anyone who has never made a mistake has never tried anything new." Therefore, I have learned many lessons through my mistakes?

Am I done learning mistakes? Not by a long shot.


Tags:#entrepreneur #fail #credit #php #html #xml #css #jquery #business #lessonslearned #server

RSS Feed

PHP Malware Problem

While working with the WePay API, I needed to get some clarification with a PHP function and decided to reference the PHP documentation at php.net. Little did I know that Chrome had some different ideas for me this morning:
PHP Malware error

This seemed very odd to me. I mean, I understand that php.net has some malware-type code on it if it is classifying PHP code as malware. Even the URI that I referenced was directly to php.net/print_r, so there wasn't any URI misdirection or rewriting or anything like that, so I am very confused.

I looked into the problem that Google provided:
PHP Malware description

That wasn't very helpful, but it did provide an insight to the issue.

I don't know why Google decided that php.net contained malware since over 100 million sites use the language, with at least 3x the amount of developers on a per-day basis?

Has anybody else experienced this kind of issue? Was my version of Chrome just derping up?

Update (2013-10-25 09:13 AM): Slashdot covered this error in particular in a discussion forum on their website. Apparently, it was a bit of compromised and altered Javascript that caused Google to flag the site.


Tags:#php #google #chrome #problem

RSS Feed

Hittin' the Tracks with Rails

Well, more like a "Ruby" and "Ruby on Rails" scenario, if you know what I mean.

Anyway, here's a comic that I wish I could create:
FatCats


Getting back to the topic, I've been going through the Codecademy's track for Ruby to understand the concepts for Ruby (I went through all 10 courses they had within 11 hours), then studied up with RailsForZombies.org and Rails Tutorial book to get a grasp on Rails. I've been working on that book for about 2 days now, and it has really shown me the in's and out's of a basic dynamic rails structure. I'm on Chapter 7 right now.

All I can say is that Ruby seems very robust, but Rails seems very picky.

It is a different standard from what I'm used to with PHP. Whereas Rails is extremely picky with "only my way works" (kind of like older HTML), I'm used ti the PHP mindset where "if you have an idea, there are about 50 ways to create it". Personally, I still like the PHP way, but I haven't given up on RoR yet, but I like the MVC procedures that Rails incorporates (and now that I can finally use git for a good reason). You can checkout my progress at http://cdn.kmp.link:3000 or even view my github releases at https://github.com/kode29/sample_app.

On the lighter side, I've created a "Skillset Chart" on my blog showing off some of my skillsets and how experienced I am with them. Just view the middle right section of my blog layout to view it.


Tags:#fatcats #rails #ruby #ror #php #html #github

RSS Feed

Python Progress: Coin Flip

Based on a conversation I had earlier today with Keat's brother, I took another whack at Python. I've been working on learning Python for a while, but never really got the hang of it since I'm mainly a PHP guy.

Anyway, based on some tutorials I learned today, I wrote a pretty nice Python script for calculating coin flips and reporting the outcome. Some of my Python-skilled friends may laugh, but I enjoyed writing this and testing my (limited) skills.

 
#!/usr/bin/python
 
import time, random, os
 

 
#clear the screen
 
os.system('clear')
 

 
#define H and T, making them floats
 
H=T=0.0
 

 
#begin input
 

 
max_num = raw_input("How many times do you want to flip a coin? ")
 

 
if len(max_num)==0:
 
	#if no input, make 10 the default
 
	max_num = 10 
 
	print "No number specificed. Defalting to 10"
 

 
max_num = int(max_num) #convert to integer to make the calcualtions work 
 

 
#little helpful tip
 
if max_num >= 300:
 
	unit = str(round((max_num*.2)/60,2))+" minutes"
 
else:
 
	unit = str(round(max_num*.2,2))+" seconds"
 

 
print "It will take "+unit+" to complete"
 

 
#flip the "coin" max_num times
 
for x in range(0,max_num):
 
	coin = random.randrange(2)
 
	if coin == 0:
 
		print "Heads" #report
 
		H+=1 #record
 
	else:
 
		print "Tails" #report
 
		T+=1 #record
 
	time.sleep(.15) #take a small break
 

 
#simple function to calcualte the percentage and report it, 
 
# instead of having to write this out more than once
 
def report(text,side):
 
	perc = round((side/max_num)*100, 2)
 
	print text+": "+str(perc)+"%"
 

 
#debugging
 
#print "Heads: "+str(H)+" | Tails: "+str(T)
 

 
#finalize the reports
 
report("Heads", H)
 
report("Tails", T)
 

 

 
#Print the winning side
 
#print "\n"
 
if H>T:
 
	print '\033[1m'+"Heads Win!"+'\033[0m'
 
elif T>H:
 
	print '\033[1m'+"Tails Win!"+'\033[0m'
 
elif T==H:
 
	print "It's a tie!"
 


I've been trying to work on getting Python to work on the web, but my server doesn't really like WSGI or mod_python. I also tried TurboGears, but that didn't quite work as expected. I may be able to work on that better and get it incoroprated to to some Dynamic programming and offer that to clients.

Also, things may be changing at Shadow Development - could be for the worse or better. Personally, I think it will be for the better because I really enjoyed pushing my skillset and coding practices, so we'll see where it goes.


Tags:#python #php #coins #heads #tails #percent #tutorials

RSS Feed

CEO Day #2

I didn't mark this last week, but as of last friday, I'm making all fridays afterwards CEO-Only Days where I push the Development side of the business to the side and work on the business instead of in the business. Here's my report from today:


  • Day started: 11:15am

  • Checked reader

  • Balanced bank account

  • Cleaned my desk off a bit

  • Took care of a customer's inquiry and renewal

  • Learned something new in Notepad++: Auto-Completion on Functions - no more having to search php.net for what order the haystack and needle go

  • added some new keywords to the Analyzer landing page based on Google's Analytics reports

  • Generated and submitted Sitemaps for a few sites

  • Found a great site: japaneseswords4samurai.com - it's not the greatest design-wise, but they have a lot of neat stuff (including Paul Chen quality swords)

  • Ran through the rest of the Kit manual

  • Created Shadow Dev specific "Client Management Process" for review

  • Started Shadow Dev specific Client followup process/script

  • Created "Design Approval Form"

  • Media Kit started

  • Started agenda for annual meeting

  • received notes for annual meeting

  • Accepted contract for new client

  • Send out task list to employees for new client

  • Lost a lead - sent out "Thanks for the opprotunity" letter

  • Made some updates to the accounting records

  • Found CancanIt.com - good analysis site

  • End: Now


That's it for me today. These CEO-only days are proving quite useful.


Tags:#ceoday #cancanit #notepad++ #php

RSS Feed

Neat QR Trick for Printing

While watching an episode of Star Trek: The Next Generation (Season 3) on Netflix, I realized a great method to implement QR codes to connect offline content to its online material.

This method is implemented in 3 steps:


  1. Put this code in your footer files (PHP only):
    echo "";

    (this uses Google's API to automatically generate the QR code for each page. If you are using ASP, insert your own code)


  2. Put this in your main CSS file:
    #print_qr{ display: none; text-align: center; }
  3. Put this in your Print CSS file:
    #print_qr{ display: block }
    and if you haven't done so already, put a print-specific meta tag to enable print-specific CSS
And that's it! Now, when your users print your pages (hopefully they go green and won't print it), they can get direct access to the page by scanning the QR code instead of having to worry about typing in the URL, if it is even on the page You are free to change anything in the provided code. I'm not ©'ing the code, just providing a concept to help others.


Tags:#qr #print #css #php

RSS Feed

What Websites are made of

RSS Feed

A Brief History of Programming

Saw this via @GeeksAreSexy - it's amazing how the Java vs PHP and Ruby duels have progress

History of Programming


Tags:#programming #java #php #ruby #javascript #fortran #cobol #basic #c #pascal #c++ #python #perl #ruby #rails

RSS Feed

I had a case of the Tuesdays

I know it's Wednesday, but this first portion deals with YESTERDAY. Deal with it. After I posted yesterday's entry, Keat and I went home. Nothing super special, but it started raining. Hard. We went to Ingles to pick up some dinner, then got back out to the car, and the stupid car wouldn't start! Since the RKE doesn't respond anymore, we have to open the car with the key, which causes a mini-alarm to go off for a bit. After about 10-15 seconds, the full-on "HEY! I'M BEING STOLEN!" alarm goes off. Which it did. For 10 minutes. The stupid key wouldn't turn in the ignition. So here we are, holding groceries, in the hard, wet rain, trying to either disconnect the car battery and/or start the car. The car finally started and we were off. Soaked, but wet.

ANYWAY, there were a few comics in the past day that I liked, so I'll be posting them throughout the week. Here's the first one.
The System

Haven't posted a System comic in a while, but what the hey - I thought I'd do it.

On to more technical stuff(s), I had an idea for a developer-friendly MySQL error notifier, since mysql_error() only works on the front end. My proof-of-concept that I developed yesterday really worked! And I'm so happy! Here it is for anybody to use.
#notify developer(s) of MySQL Errors
 
#use: $result=mysql_query($sql) or die("Oops!".mysql_dev_error($sql));
 
#(c) 2011 - Shadow Development [http://shadowdev.com]
 
if (!function_exists(mysql_dev_error)){
 
function mysql_dev_error($sql){
 
        #get the php-generated mysql error
 
        $error=mysql_error(); 
 

 
        #get the database name
 
        $db_q=mysql_query("SELECT DATABASE()"); 
 
        list($db) = mysql_fetch_array($db_q);
 

 
        #get the top-level domain along with the page the 
 
        ## query is being executed on
 
        $page=$_SERVER['SERVER_NAME'].$_SERVER['PHP_SELF']; 
 

 
        #send HTML email
 
        $headers  = 'MIME-Version: 1.0' . "
 
";
 
        $headers .= 'Content-type: text/html; charset=iso-8859-1';
 
        $headers .= "
 
";
 

 
        #generate message
 
        $message ="SQL Query:
$sql

"; $message.="Database:
$db

"; $message.="Error:
$error

"; $message.="Page: $page"; #send off mail("DEVELOPER_EMAIL", "MySQL Error for ". $_SERVER['SERVER_NAME'], $message, $headers); }}

BTW: Creative Commons License
MySQL Developer Error by Kyle "KP" Perkins - Shadow Development is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

I'm really surprised how well it works for just a proof-of-concept. I told Keat last night that I must be getting really good at this "programming" thing. I used to think of an idea, write it down, write the code in 15 minutes, then spend 2 weeks debugging it. Now, my 15-minute code sessions work like a charm from the get-go. I must be doing something right....

Here's the overview of what I got done today:
  • I got some minor edits done to the Shadow Dev Beta blog (added a side navigation like the on one this blog; had to make my own URL shortener for some URL-based comments).

  • Made some additions and edits to the Receipt Rescue.

  • Finally sent out May's newsletter and followups (I'm only 3 days late!).

  • Contacted a potential client to find out that my idea of a "potential client" turned into "We need an editor" - no fun.

  • Made a minor adjustment to my Cron job from last week (decided to have the IP addresses added to a DB log and see which ones were repeats - so far, it's blocked 29 addresses in just over a week; that's about an hour of free time I got back!).

  • Finished Rocko's Modern Life on Netflix (first 3 seasons were good, the last season got a little weak, like most final seasons do).

  • Added a Microtimer to the Main and Beta Shadow Dev sites to see the page generation time.

  • Found out about EchoSign.com - an electronic PDF signer for clients. Not a bad idea, and I may try it in the future.

...and that's about it for now. Till tomorrow!


Tags:#thesystem #php #creativecommons #mysql #car #alarm

RSS Feed

Snow Day? Snow Way!

xkcd
(Snow comic for the Snow day)


Sorry for the bad pun, but that's the way it seemed today. We saw that it was snowing last night, so Keat got up a little early and found out that classes were on a 2-hour delayed schedule. So what did we do: we slept for 2 more hours. We finally got up at around 9:30 and got our things together to leave. I didn't want to get out of the comfy, thick, warm covers. I thought "the world can end before I'll get out," but Keat had to get to class.

There was snow on the ground, and more falling every minute. My hands were about to freeze off. After working up the courage to brave the cold, we went to my car only to find out that my doors were frozen shut. What luck. I went inside and got a pitcher of water to pour on the ice. I went back outside, and Keat had the passenger doors open. I guess that side was facing the sun. I focused back on the pitcher, and poured it on the driver-side windows and doors. After some wiggling, the doors finally opened and I started up the car. It was a little squeaky due to the cold, but we made it to HCC.

I dropped Keat off at class and I went to the Mill Pond to take some pictures of a snow-covered mill house on a pristine lake. What a picturesque moment! Too bad my phone doesn't take very good long-distance photos. I was driving to the office when Keat called me saying her class was over. Apparently, the teacher was just collecting papers. I went back to her and dropped some more stuff off. Then, I went to the office again.

I caught up on my email, RSS feeder, and other related interests. A few main things I wanted to take care of were the Uptime status report on the ShadowDev.com site, "Latest Comments" on the blog, and a few other items.

According to Pingdom, the new server has a surprisingly decreased downtime as compared to the old server. The report said that the old server had a 99% uptime ratings, and so far, the new server has a 74% uptime. So much for the guarantee...

I found out that there were a few things that didn't transfer from the old server to the new server, including some of the recent changes I made to the blog. I played "code catchup" for a few things (including re-referencing the format_link alrorithym from the old server to the new; apparently, the new server couldn't find the right file and couldn't parse the functions, so the Twitterfeed fetcher and RSS maker wouldn't work), then worked on the "Latest Comments" section, which took the most time. I put in the following code to retrieve the title of the responded entry the comment was left for:
$sql="select `title` from `journal` where id=$id";
 
list($title)=mysql_query($sql) or die("Error 30: ".mysql_error());
 
echo $title;

and ran the code. It didn't turn up what I expected. As a matter of fact, it didn't turn up anything! The source code, the output buffer, the error reporting...all were blank. I spent the next hour trying different things to figure out the issue. I finally gave up and referenced the PHP online documentation. What I found made me felt stupid. I had to add one line to the above code for it to work properly:
$sql="select `title` from `journal` where id=$id";
 
$result=mysql_query($sql) or die("Error 30: ".mysql_error());
 
list($title) = mysql_fetch_array($result);
 
echo $title;

Wow, did I feel stupid. I put that in, and it worked like a charm. Only then did I find out that there were 4 spam-based comments that I had to moderate.

Keat got out of class, we had lunch, then back to the office. We also found out that the next time our neighbors' dog starts being mistreated, we can call the Asheville Police and file a complaint, which allows the landlords to file a violation as well. This is great because now we have a plan of action instead of wildly guessing our next step.

Anyway, while she worked and took a nap, I watch Danny Phantom, Season 3 (which I have not seen before) and worked on the Expo report (which is due tonight) and the VIM coloration issue. On the old server, the VIM editor would automatically add colors to the proper code segments while editing. The new server didn't support this, and it was getting very confusing when I would edit files. After searching Google for about 10 minutes, nothing turned up. Apparently, I was calling it by the wrong name and should have been searching for "Syntax Highlighting." Long story short: the VIM version I had (7.0) was compiled with the TINY option, which is basically a minimalized installiation. I tried to re-compile it, but the configure file was missing. So, I tried to update it via yum, but yum said it was up-to-date. The latest version was 7.3, so I knew something was off. I downloaded and compiled VIM 7.3 and compiled it with everything under the sun. Therefore, I was guarenteed to get the Syntax higlighting I so wanted. After some initial testing, I also found out that the command vi was defaulted as a minimalistic editor (7.0), while vim is the full-fledge editor (7.3) with syntax highlighting included. So I fixed a few bugs and have the most up-to-date version for editing, along with a backup for emergency fixing. No harm done.

I then decided to check Facebook (for the heck of it), and while trying to get used to their new profile layout, saw that a friend of ours was having some trouble on the snowy roads in Asheville. I asked her to keep us updated, becasue we still have to come home. We had a small discussion and she posted photos of the snowy roads. After I saw the photos, all I thought was "Aw crap." Based on her reports, the roads were snowy and icy, and people were sliding all over the place. And me without my current car insurance card (it's in the mail and should be here within the next few days). Keat and I decided to try to find her teacher so she could possibly miss class if need be. We frantically searched the traffic reports and saw that I-40W was slow and I-40E was clear. However, that didn't say how the road conditions were. I was thinking back to last year when Keat and I had to walk to the nearest working grocery store, and how I didn't want to drive through that.

We drove to her school and tried to find her teacher. The roads weren't that bad (although that was a 5 mintue drive). After searching and waiting for about 45 minutes, the teacher finally arrived and wasn't too sure about class. We then decided to gain the courage and drive home. Keat didn't want to stay the night at the office.

We got on the highway and I followed a truck for about 10 miles. Thinking the roads got worse the further we got near the city, I was mentally prepared to face the icy roads. We finally passed the truck I was following since the roads weren't that bad (just a little snow dusting), and kicked it up the rest of the way home. Turns out the roads in West Asheville weren't bad at all, just had a little dusting. The roads in South Asheville had the brute of the storm and had icy roads. Luckily, we weren't in that area.

We got home, took care of the kitties, and Keat worked on a presentation and paper she had due. My laptop was used to catch up on our shows, and I played PS2 while I waited.

Chi's still in heat and it's driving us nuts! We are going to call the spaying service and get an appointment asap!

The temperature is in the mid-teens (with a wind chill of 3!) and there is a light flurry outside. This isn't right for NC! I just wonder how things will be different tomorrow with the class schedule. I still have to get the Expo report done and send it in before the SMDC meeting on the 8th. Wish me luck!


Tags:#xkcd #snow #class #pingdom #downtime #mysql #php #yum #vim

RSS Feed

Good news Everyone! Server transfer is being finalized!

Working Daze

Today had to be one of the shortest working days I ever had. Before I get into the day's activities, I received an email from the support guy yesterday (Monday) at 1pm saying that the missing databases were up, and he could move the subdomains for me. I sent him a list of the subdomains I needed moved (send around 1:30pm). I also found out that accessing PHPMyAdmin was tedious (see previous post), and sent another email around 8:30pm. On to Tuesday:

Everytime my alarm went off (at 9am, 9:30, and 10), I checked outside to see if the "Inspector People" were here yet. Luckily, they weren't. I got out of bed around 10:30, got dressed, unloaded/loaded the dishwasher, did some last minute cleaning, put the cats in the bedroom (with litter, food, water, and scratchers), and left the apartment just before 11 to get Keat to class. After dropping her off, I arrived at the office at 11:45 and booted up. The server transfer was supposed to be completed Monday night/Tuesday morning, so I wanted to check on it. Well, it wasn't exactly finished. A few of the sites weren't even transfered! I received this email at 12:15pm today:
It seems like all the sub domains are redirected to one of the sub net of shadowdev.com. I think you can log on the control panel and add them. Most of them don't have any content. you know where they redirects to. It will be easier that you add and set them up on the httpd.conf file which locates at /var/www/vhosts/shadowdev.com/conf
..and this about the PHPMyAdmin access:
I am not sure if you can access to phpMyAdmin in the other way. That is how i usually do which i have to log on the control panel and do it.
I sent in a response saying that I actually completed the subdomain transfer (which took about an hour), and asked about the shadowdev.com transfer (send at 1pm). Also, I checked the other domains to see if they were transfered, and found out that only 5-6 were transfered and the rest were not. I asked support about what was going on (at 1:15pm). This was the first time I was a little suspicious about my support guy's education and training. I mean, not to stereotype anybody, but when the support email says:
Hello,
 
Thank you for your email.
 
The reason i did not switch them because there are so many sub domains attaching with a couple of domains. I just switch them all to the new server now. There might be a few setting which are not configured properly (old configuration). Let us know if you need root password so you can easily configure the php files. This the full path where each domain locating: /var/www/vhosts/ This is where the website content locates: /var/www/vhosts/domain.com/httpdocs

...you know there has to be something up. My webhosting company hasn't really been the best webhosting company in the world, especially with some of the previous support requests I sent in the past. However, this is the only company that gave me a good price for the services I needed. Other companies were either lacking in equipment, software, or too-high price.

Anyway, I finally found out that the shadowdev.com domain was transfered (or so I hoped), and I downloaded the PHPMyAdmin software to its own subdirectory on the server. After loading the setup domain and fixing a few requirement files, I had quick and easy access to PHPMyAdmin, just like the old server. It's not exactly the same, but it's better than the 5-step process. You think that the support guys would know that. Oh well.

I left the office around 2:15 to pick up Keat, we quickly picked up some gas and food, and went home. It turns out that the "Inspector" guy had been there, and left a little 1" strip on the counter saying "Maintenance was in your apartment today performing the semi-annual safety inspection. Thank you for your cooperation." I guess we passed.(?)

We let the kitties out and about, and they love the clean(er) apartment. Keat and I actually sat down and watched Date Night (which was good; 8/10 overall). Afterwards, I worked on fixing server migration issues and found out that either the hosting company's servers were getting confused, or the worldwide DNS servers were getting mixed records, because wheverI would go to a site (for example: shadowdev.com), it would either load the old server or the new server (the sites on the new server have "New Server" comments at the top of the main index page so I could tell the difference, but end-users couldn't tell). It got really confusing when I had to upload or edit certain files to fix specific errors (hence: the "finalization" process).

Keat took a small nap while I did that, and then I cooked dinner. Afterwards, she woke up and we caught up on some of our Hulu episodes while eating, and by the time we realized it, Tuesday was over and December was here.

On the lighter side, I found out that Yanni (my favorite New Age artist) is releasing his first instrumental album since 2003, and it comes out in February. Maybe early Birthday present for myself? That'd be nice.

Along with the server location confusion, the top-level domains (like shadowdev.com) point to the new server, while subdomains (like dev.shadowdev.com and kp.shadowdev.com) point to the old server. I'm not an expert on server setups, but that gets really confusing, especially since the DNS records were never manually modified for remote subdomain locations. I'm posting this entry on both servers so both have the most updated records no matter what the DNS/cache servers say.


Tags:#server #workingdaze #inspection #support #dns #phpmyadmin #yanni

RSS Feed

Server transfer almost done!

Dilbert

A lot happened today, but I want to keep this short so I can recreate it if need be; the server transfer is almost done and the last thing to be transfered is the shadowdev.com core. That pretty much means 85% of the files on the web server (including this subdomain). I can't wait till the transfer is done so this headache can be over.

I already vented to Keat, so the majority of my 'rant' is over. However, I'll sum it up for quick-reading purposes. I:
- added Hugh to the Advisor list on the Shadow Dev sites and sent out a short PR announcing his Advisor-ship;
- sent Dad the Alpaca information, only to find out that he already met the people and he's not pursuing that course of action;
- called Progress Energy and found out that the payment station didn't scan the account number on my check right and charged me 13.50 + my payment, but the operator was very friendly and helped me through the process, including paying online and avoiding the stupid $4.95 online processing fee;
- called the Auto shop and finally rescheduled my tire rod appointment that I had to postpone 2 weeks ago; added ALL of the email forwarding rules to the new server;
- finally figured out the stupid open_basedir issue on the new server and solved that (long story short: had to manually edit the new httpd.conf files one-by-one); semi-caught up on Eureka Season 4 (ep. 5-9, until 1-4 become available and/or episodes 10-20 air);
- manually updated ALL of the databases on the new server (the support guy was transferring them one-by-one, which was taking forever, so I exported the info out of phpMyAdmin on the old server and imported it on the new server; est. time of competition: 1 hr);
- found out that the article that The Mountaineer came to us for back at the beginning of the month is scheduled to be published on Wednesday (yay!);
- talked to Allan about the SMDC Expo event and got some of the 'minor' details out of the way;
- re-croned the new server to perform the "reminder" cron job every minute of every day (after I realized that the main php module was in a different location;
- loaded Seesmic on my laptop, which is easier to manage than TweetDeck, but I would like the integrated desktop feature for constant notifications of mentions and replies;
- stopped by the Waynesville Rec center and took a tour - really like it and may join in the next few months;
- browsed the "Internets" and found a lot of good stuff: duly tweeted
- and of course, did my standard stuff for every day (check reader, reply to email, etc);
- then came home and did some last-minute cleaning before the inspection tomorrow
....And that was my Monday.

Can't wait to see what Tuesday brings up!


Tags:#dilbert #smdc #php #tweet #cron #expo #progress #transfer #subdomain #alpaca #eureka #hugh