Sometimes the text files are very large and you don’t want to load it in memory before searching through it because it’s inefficient. You just want to get the last few bytes of the file because the latest additions / updates are there.

This would help you:

$fp = fopen('somefile.txt', 'r');
fseek($fp, -50, SEEK_END); // It needs to be negative
$data = fgets($fp, 51);

Which gets the last 50 bytes of the file somefile.txt in $data. Thanks to niels for the solution.

Make sure the 2nd parameter for fgets() is 1 more than the absolute value of the 2nd parameter for fseek().

{ Comments on this entry are closed }

Apache is a marvelous web server that offers .htpasswd and .htgroup for controlling restricted access to your website. By help of .htaccess, they work as a member login & user management system that is so simple and easy to deploy. You can even define user groups / roles with it.

Basically,

  • .htpasswd defines pairs of username & password that are user accounts.
  • .htgroup defines groups / roles of user accounts that can be access-controlled as a whole.
  • .htaccess then applies .htpasswd and .htgroup to the current directory, and specifies which groups in .htgroup has access to the current directory.

For example, we have

/home/myuser/.htpasswd

user1:{SHA}kGPaD671VNU0OU5lqLiN/h6Q6ac=
user2:{SHA}npMqPEX3kPQTo+x/+ZckHDrIcQI=
user3:{SHA}q1Fh2LTUjjkncp11m0M9WUH5Zrw=

/home/myuser/.htgroup

admin: user2
editor: user1 user3
writer: user3

/home/myuser/public_html/example.com/member/.htaccess

AuthName "Members Area"
AuthType Basic
AuthUserFile /home/myuser/.htpasswd
AuthGroupFile /home/myuser/.htgroup
<Limit GET POST>
require group admin
require group writer
</Limit>

What they do is only let users in the admin and writer group, that is user2 and user3, to access example.com/member. When someone tries to access example.com/member, Apache would prompt him or her for user name and password, and he or she must be either user2 or user3 to access it – they must enter the correct password set out in .htpasswd for user2 or user3.

user1 isn’t allowed to access example.com/member even if the password is correct. You get the idea.

You can place .htaccess anywhere in your website, and it will control access to the directory it’s in by the defined rules (which groups / roles are allowed to access). Just make sure it is pointing to the right .htpasswd and .htgroup by AuthUserFile and AuthGroupFile.

And you can have multiple .htaccess in different directories of your website, using the same .htpasswd and .htgroup.

This is so simple yet so very handy in creating & managing different users and user roles (.htpasswd, .htgroup) and giving them permissions (.htaccess) in accessing different website assets.

PHP Class

Now that you are familiar with the basic authentication and native user management system in Apache, you can use this two simple PHP classes to automate tasks such as user creation, user deletion, adding user to group, and removing user from group.

class Htpasswd

class Htpasswd {
	
	private $file = '';
	private $salt = 'AynlJ2H.74VEfI^BZElc-Vb6G0ezE9a55-Wj';
	
	private function write($pairs = array()) {
		$str = '';
		foreach ($pairs as $username => $password) {
			$str .= "$username:{SHA}$password\n";
		}
		file_put_contents($this -> file, $str);
	}
	
	private function read() {
		$pairs = array();
		$fh = fopen($this -> file, 'r');
		while (!feof($fh)) {
			$pair_str = str_replace("\n", '', fgets($fh));
			$pair_array = explode(':{SHA}', $pair_str);
			if (count($pair_array) == 2) {
				$pairs[$pair_array[0]] = $pair_array[1];
			}
		}
		return $pairs;
	}
	
	private function getHash($clear_password = '') {
		if (!empty($clear_password)) {
			return base64_encode(sha1($clear_password, true));
		} else {
			return false;
		}
	}
	
	public function __construct($file) {
		if (file_exists($file)) {
			$this -> file = $file;
		} else {
			die($file." doesn't exist.");
			return false;
		}
	}

	public function getUsers() {
		return $this -> read();
	}
	
	public function addUser($username = '', $clear_password = '') {
		if (!empty($username) && !empty($clear_password)) {
			$all = $this -> read();
			if (!array_key_exists($username, $all)) {
				$all[$username] = $this -> getHash($clear_password);
				$this -> write($all);
			}
		} else {
			return false;
		}
	}
	
	public function deleteUser($username = '') {
		$all = $this -> read();
		if (array_key_exists($username, $all)) {
			unset($all[$username]);
			$this -> write($all);
		} else {
			return false;
		}
	}
	
	public function doesUserExist($username = '') {
		$all = $this -> read();
		if (array_key_exists($username, $all)) {
			return true;
		} else {
			return false;
		}
	}
	
	public function getClearPassword($username) {
		return strtolower(substr(sha1($username.$this -> salt), 4, 12));
	}
	
}

class Htgroup

class Htgroup {
	
	private $file = '';
	
	private function write($groups = array()) {
		$str = '';
		foreach ($groups as $group => $users) {
			$users_str = '';
			foreach ($users as $user) {
				if (!empty($users_str)) {
					$users_str .= ' ';
				}
				$users_str .= $user;
			}
			$str .= "$group: $users_str\n";
		}
		file_put_contents($this -> file, $str);
	}
	
	private function read() {
		$groups = array();
		$groups_str = file($this -> file, FILE_IGNORE_NEW_LINES);
		foreach ($groups_str as $group_str) {
			if (!empty($group_str)) {
				$group_str_array = explode(': ', $group_str);
				if (count($group_str_array) == 2) {
					$users_array = explode(' ', $group_str_array[1]);
					$groups[$group_str_array[0]] = $users_array;
				}
			}
		}
		return $groups;
	}
	
	public function __construct($file) {
		if (file_exists($file)) {
			$this -> file = $file;
		} else {
			die($file." doesn't exist.");
			return false;
		}
	}

	public function getGroups() {
		return $this -> read();
	}
	
	public function addUserToGroup($username = '', $group = '') {
		if (!empty($username) && !empty($group)) {
			$all = $this -> read();
			if (isset($all[$group])) {
				if (!in_array($username, $all[$group])) {
					$all[$group][] = $username;
				}
			} else {
				$all[$group][] = $username;
			}
			$this -> write($all);
		} else {
			return false;
		}
	}
	
	public function deleteUserFromGroup($username = '', $group = '') {
		$all = $this -> read();
		if (array_key_exists($group, $all)) {
			$user_index = array_search($username, $all[$group]);
			if ($user_index !== false) {
				unset($all[$group][$user_index]);
				if (count($all[$group]) == 0) {
					unset($all[$group]);
				}
				$this -> write($all);
			}
		} else {
			return false;
		}
	}

	public function getGroupsByUser($username = '') {
		$all = $this -> read();
		$user_groups = array();
		foreach ($all as $group => $users) {
			if (in_array($username, $users)) {
				$user_groups[] = $group;
			}
		}
		return $user_groups;
	}

}

Usage

First, you should use your own $salt. Change the value of $salt in the Htpasswd class to something else for your own application.

To instantiate the classes:

$passwdHandler = new Htpasswd('/home/myuser/.htpasswd');
$groupHandler = new Htgroup('/home/myuser/.htgroup');

To create and delete a user:

// Add a user with name 'user1' and password 'I prefer to use passphrase rather than password.' if it doesn't exist in .htpasswd.
$passwdHandler -> addUser('user1', 'I prefer to use passphrase rather than password.');
// Delete the user 'user1' if it exists in .htpasswd.
$passwdHandler -> deleteUser('user1');

To check if a particular user exists in .htpasswd:

// Check if user 'user1' exists in .htpasswd.
if ($passwdHandler -> doesUserExist('user1')) {
	// User 'user1' exists.
}

To get the clear text password for a particular user (Apache stores passwords in .htpasswd as encoded strings):

// Get the clear password for user 'user1'.
echo $passwdHandler -> getClearPassword('user1');

To add a user to a group:

// Add user 'user1' to group 'admin' in .htgroup. Group will be automatically created if it doesn't exist.
$groupHandler -> addUserToGroup('user1', 'admin');

To delete a user from a group (user still exists in .htpasswd, just not associated with the group any more):

// Delete user 'user1' from group 'admin' in .htgroup. Group will be automatically removed if it doesn't contain any users.
$groupHandler -> deleteUserFromGroup('user1', 'admin');

To get a list of groups a particular user is assigned to:

/* Get an array of groups that 'user1' is a member of. */
$user_groups = $groupHandler -> getGroupsByUser('user1');

Conclusion

This ain’t concluded. It’s just an END notice. Feel free to let me know your thoughts and how my classes work for you.

{ Comments on this entry are closed }

I have previously reviewed Moredays, the cool productivity tool that is also fun, right after its Beta launch in October last year.

Since then, the digital organizer and scrapbook in one has continued improving, adding in for example the ability to sync with Google Apps, that now makes it pleasure to use. They’ve stayed true to focus on photos and graphics, which alone would warrant a second try, but if you were still hesitant, the soon-to-be-released native iOS apps should make you change your mind.

moredays screens blog on iphone

Moredays focuses on bringing the best user experience for each device. Their iPhone app makes the most of the small screen, letting you easily check what’s on your agenda for any given day or add items on the fly.

The iPad version, on the other hand, seems like a great tool for someone eager to take notes in a meeting or get an overview of the whole week ahead.

moredays

Both of these apps are in final stages of development and testing and will be released in the next few weeks. Will you be checking them out?

{ Comments on this entry are closed }

Avalanche Coupon Code (Magento Theme)

by Yang Yang on February 29, 2012

Avalanche Magento themeJake (fastdivision.com) was kind enough to offer me a coupon code so that my readers can enjoy the Avalanche Magento theme at 15% discount. In case you haven’t read my review of the Avalanche theme, it is the current sensation going on in the Magento-sphere. In the shortest words, I think it’s the best Magento theme because of 3 reasons:

  1. It is built by someone who’s insanely good at the website stuff (HTML, CSS, PHP, design, user experience, etc.). and who pursues perfection.
  2. It has unparalleled support – read my review and you will know. Better yet, join the club to experience yourself!
  3. The theme is very very good (best is a pretty strong word and I’ll leave it to you) in all aspects I can think of AND it’s being pushed to be better every day. Read Jake’s blog. He’s just finished working on and released a mobile version of Avalanche.

Coupon Code for Avalanche

OK here’s the coupon part. If you have made up your mind to purchase Avalanche, use this coupon code when checking out:

KAVOIR
Click To Open/Copy

Just enter it in the “Enter coupon code” text box at the end of the checkout page:

Avalanche coupon code for 15% off

And you will have the 15% discount off the normal pricing of $199 and $399 which would then be $169.15 and $339.15 respectively. Always nice to save a few bucks.

Enjoy!

{ Comments on this entry are closed }

Cheapest GoDaddy Promo Code – $0.99 / Year .COM

by Yang Yang on February 24, 2012

Just found out some dirt cheap GoDaddy coupon code for new .com registrations:

INDY500 - $0.99 for 1st year
FLOWERS - $1.49 for 1st year
leap149 - $1.49 for 1st year

Which enables you to get any new .com name for just $0.99 – $1.49 for the first year. Not sure if you can claim it more than once per account but I believe one can’t. I used it to quick register one .com domain and never tried it a second time.

This is probably the cheapest GoDaddy coupon code ever for new domain registrations.

$2.95 / year .COM from GoDaddy

Another tip for getting cheap .com names from GoDaddy is this one:

http://www.godaddy.com/domains/search-danica.aspx?isc=goft006ec

It gives you the opportunity to register any new .com names for as many years as you want at just $2.95 per year (plus ICANN fee). That’s right, for just $15, you could have a .com name for 5 years. At normal pricing, $15 would only get you one or two years.

I found out this one by discovering one of the GoDaddy AdWords ads. They give amazing offers to traffic from AdWords campaigns.

In my opinion, though $1.49 may seem a lot cheaper than $2.95, I’d still opt to go with the $2.95 / year route because I can register multiple years. $1.49 would just get you locked in and then pay normal next year and so on. GoDaddy doesn’t release many renewal coupon codes as it does new registration codes.

$7.49 / year .COM renewal code

Zine10

This code gives you $7.49 / year renewal price for .com names at GoDaddy. It’s said to have been working for at least the last 2 years so hopefully it would go on working.

{ Comments on this entry are closed }

Webmium Review – WYSIWYG Website Builder

by Yang Yang on February 23, 2012

Webmium 40% Discount Promo Code: KAVOIRWebmium may not have much to look at on their website – for which they better hurry up and get a better design – it’s actually very very easy and intuitive to use.  With adequate features, abundant site templates to choose from, and hundreds of online marketing guides, it’s the combo solution for people who want to get their business website up in less than 10 minutes, without having to spend an arm and a leg to tackle the challenges of web design, web development, web hosting, and web marketing. Webmium has it all.

Unbelievably Easy and Intuitive

After a few minutes of clicking and typing, my first site on Webmium was up and running – Xi’an Jobs. I didn’t have to think once to get my job done. Everything seemed to be right there when I needed it. I felt like a breeze being pushed by the wind, naturally and comfortably and never did I have to sweat any effort to accomplish the whole website which is decent enough for most undertakings. Your site is well modularized so that you can freely switch between themes / layout and move around objects without jeopardizing anything. Your content is never hard coded.

A WYSIWYG Website Editor Better than Desktop Programs

Everyone can learn and start doing it in a minute. In fact, it’s so intuitive and easy that everything including editing text, uploading image, adding contact form and creating another page is accomplished within one editor page. Check out the editor screenshot:

webmium website editor

You don’t get any more WYSIWYG than this. Lots of desktop website creator programs such as Adobe Dreamweaver and Microsoft Expression Web available on the market who claim to be WYSIWYG don’t come up close. You have to view the produced web page in a browser to see the final results. But with Webmium, it’s all in there, 100% WYSIWYG and never again do you have to scratch your head and ask why the hell does it look so different in the browser from that in the editor.

Free Entry Plan – Very Good for SEO

Best of all it’s free and they’ve got tons of FREE templates to choose from. You should get one up on Webmium right now. Sign up with them. It’s definitely worth the time to have an external page of content up for the sake of SEO. Be sure to create useful, original and rich content or Google won’t buy it just because it’s on a distant IP. Webmium has an auto-submission feature that after you have published your site it will automatically submit the site to search engines like Google.

My tiny site about Xi’an Jobs ( http://xian.webmium.com/ ) was indexed by Google 8 hours after being published at Webmium. Definitely looks promising from the perspective of SEO.

Quick Built-in Guides to Kick Start Your Online Marketing

For small business owners who don’t know how to have their own website online, Webmium is the no-brainer choice. It has got hundreds of articles and guides in the Marketing Academy on how to do it and what the best practices are, about not only the technical aspects but also the marketing part. It’s a perfect companion for starter small businesses owners who want to have their website up today, in the minimum effort and cost possible. And you are well guided along the way with built-in tips while you are building your online presence.

What can be improved about Webmium?

It’s a greatly simple and useful solution for starter business owners but there are definitely ways that Webmium can be improved. I’ll just lay out a few of the problems I met when playing with it:

  1. Currently there’s no way I can add text beside the logo. It’s either a logo or a text but you can’t combine.
  2. I don’t seem to be able to align social buttons such as facebook like buttons and tweet buttons together. Weird.
  3. Unable to modify footer attribution to add copyright notice, etc. I guess it’s because I’m on the free plan. You would probably be able to do that after upgrading to the PRO plan.
  4. For now, each account is limited to one website / domain. I think they can definitely offer the ability to create and manage multiple websites under one account in the PRO plan.

Other than these, Webmium is looking a prosperous future if they keep shaping it better.

Upgrading to PRO? Use a Promo Code!

Webmium was kind enough to offer an exclusive promo code to the readers of Kavoir.com. If you want to take advantage of the PRO plan that offers all the easiness to build a small business website as well as unlimited storage and bandwidth, use promo code:

KAVOIR
Click To Open/Copy

Like this:

Webmium Promo Code

When you are upgrading but before making payment. It would give you a nice 40% discount off the listed price.

{ Comments on this entry are closed }

PHP: Strip or Trim Extra Whitespaces Inside a String

by Yang Yang on February 14, 2012

There are raw strings that contain extraly unwanted whitespaces (tabs, spaces, new lines) that you want to get rid of so as to form a normalized string that has only a single spaces between words and sentences. For instance, you may want to transform this string:

Morning   is here,   sunshine
   is here.    Morning is    here.

Into this:

Morning is here, sunshine is here. Morning is here.

How to achieve this in PHP?

Just use this function:

function purge_inside_whitespaces($text) {
	while (preg_match('/\s{2}/', $text)) {
		$text = preg_replace('/\s{2}/', ' ', $text);
	}
	return $text;
}

Basically, it keeps scouring through the $text string passed to it and removes any continuous whitespaces and replace them with single spaces, until it can’t find any more continuous whitespaces.

Note you would need to apply trim() to get rid of any whitespaces in the beginning or at the end of the string, because if there are any there, a single space would still remain after applying purge_inside_whitespaces().

{ Comments on this entry are closed }

Inspirational Music & Uplifting Tunes

by Yang Yang on February 8, 2012

Inspirational Music

People close to me would know that I play piano for fun – not an advanced player but the best I can do is Beethoven’s Moonlight Sonata (only the 1st movement, -_-). I think everyone could learn one or two musical instruments and amuse themselves. Music is such a great endowment to us. It dispels the dismay, enriches our emotions and reignites the dreams for a beautiful life. A week ago I found some very nice music tunes at Audio Jungle (link to their most popular pieces) that is so inspirational and upbeatly pleasant. I can’t help but being motivated to be happy and creative. They are so simple yet so masterly composed. Listed below are what I like most. Hopefully they would resonate in you as well.

Metro Light MusicLive My Life

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/live-my-life/224500

MelodalityA Fortunate Day

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/a-fortunate-day-extended-version/72668

Audio QuattroClouds

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/clouds/95370

Sweetwave AudioSeeing is Believing

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/seeing-is-believing/854045

SoundrollA Way To The Top

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/a-way-to-the-top/162200

BeatheBeatPositive Thinking

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/positive-thinking/94451

Tim McmorrisTranslation

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/translation/1299157

DirtyflintEnergetic Corporate Pack

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/energetic-corporate-pack/1197765

MelodalityBusiness Music Pack

Audio clip: Adobe Flash Player (version 9 or above) is required to play this audio clip. Download the latest version here. You also need to have JavaScript enabled in your browser.

Source: http://audiojungle.net/item/business-music-pack/108020

 

For people into classical music, here’s one of my sites: Top Classical Music. It tries to collect the best pieces of classical music arranged by authors and their epoch. Since fine MP3 files with impeccable quality are hard to come by, it’s very far from complete. I’ll try and get more pieces on the site when I can.

Update: Some more music tunes I like from AudioJungle

{ Comments on this entry are closed }

It is common to import CSV files into MySQL database. You can do this with phpMyAdmin with small CSV files but with large ones, you would probably encounter the memory error and had to switch to MySQL command line “LOAD DATA LOCAL INFILE” to do the job.

It looks like something like this:

LOAD DATA LOCAL INFILE 'your.csv'
INTO TABLE `your_table`
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
(field1, field2, field3)

And then you encounter another problem that it only imports the first 1 or 2 rows and then it stops. After some researching and trying, I was sure it’s something to do with the “LINES TERMINATED BY” directive. Depending on the platform that the CSV file is created on, the line delimiter may be

  1. \n
  2. \r\n
  3. \r

And you need to be correct on the line delimiter to properly parse the CSV file.

So the solution is to try them all one by one and see which one of them works. Chances are, one of them would make the whole command successfully import ALL rows.

Another simple approach is to deprive the whole command of the “LINES TERMINATED BY” directive and let MySQL do the call. It’ll probably detect things right but in my case, this doesn’t work but specifying ‘\r’.

{ Comments on this entry are closed }

Let’s be honest here. One of the most dreaded dreams for any working professionals perhaps is generating timely and professional invoices. Important as they are for ensuring timely payments, it is a pain to sacrifice time and make an effort to track down all the details. If you are having a tough time preparing invoices and the accompanying reports, a valued solution that lies with you is Invoicera. Invoicera is a completely web hosted application that effortlessly works towards getting the invoice generation job done, in a professional manner.

A sturdy online invoicing and billing application, Invoicera provides you with a self explanatory user interface along with poignant and much sought after set of features and functionalities. Thus it seamlessly simplify and streamline the entire process of generating and sending invoices.

Invoicera control panel

Language Multiplicity and Multicurrency support

This is a major factor, which gives Invoicera an edge over various other online billing systems in the market. Invoicera supports 11 different languages and more than 100 currencies.

Effortlessly Manage Your Clients

In the client section apart from managing and adding your new clients you can also view their account statements and create custom fields and entries which you deem necessary.

To overview the complete details of any particular client there is an automated Report generator, which gives you the ease of analyzing the dealings and business you have had with the customer.

Incredible Time Tracking

With its expert time tracking capabilities, you can be rest assured to track down every minute of your time that you have spent on the projects for your clients. It completely nullifies all the worries surrounding time tracking and you can easily keep a check on how much time you have spent on a particular project.

Template Customization Service

In case you are not technically trained in customizing the look of the invoice / setimate templates, Invoicera provides you with the option of Invoice Template Customization service. This will provide you with the expert services of seasoned technical experts and you can easily customize the look of the invoice that you send, as per the need of your business.

Schedule Your Invoices Easily & Charge Late Fee from Defaulters

A stand out point of Invoicera is that unlike other similar apps, with Invoicera you can easily schedule each of the invoices needed to be delivered. You can simply schedule the date and time on which you need to send out the invoices to the users and the intuitive online invoicing solution that Invoicera is, it will make sure that invoices are delivered right on scheduled time. Besides, with just a click of the mouse, you can add the late fee charges and get reimbursed for the same.

Other Benefits That Invoicera Offers:

  • Login and carry out some invoicing tasks wherever you have access to the Internet either via PC or laptop,
  • Flexible pricing depending on your usage levels,
  • Constant updates and support,
  • No large up front application costs,
  • Data security and backup

In Short

Invoicera is vast enough to confound all the different invoicing requirements of your web site and you assure yourself productive gains by availing its services. Besides, their customer support and related services are impeccable and unmatched. Thus if at any time you stumble, Invoicera provides you with enough options to continue the smooth sailing.

Invoicera - online invoicing

{ Comments on this entry are closed }