Payment of ₹ requested by HONEY VIG. Click the link to pay securely via Razorpay: Make 5000 payment Payment of ₹ requested by HONEY VIG. Click the link to pay securely via Razorpay: Make 5000 payment ⭐ If you would like to buy me a coffee, well thank you very much that is mega kind! : https://www.buymeacoffee.com/honeyvig
Hire a web Developer and Designer to upgrade and boost your online presence with cutting edge Technologies
Showing posts with label web. Show all posts
Showing posts with label web. Show all posts

Wednesday, September 30, 2026

Botnado Is Coming for Your Platform

 

Matt Dunphy, a Lead Solutions Engineer at commercetools, posted a story on LinkedIn that reminded me that Reddit has been a dumping ground for ChatGPT content and is turning into a spam ground for everything, and he inspired this Botnado edition of my newsletter, or maybe the approaching Boticane, or maybe we are in the eye of the storm. (Note to self: explain to the readers the movie Sharknado)

The story he posted, a team "currently paused on a commercetools implementation as the complexity came in higher than scoped," reads as if a merchant halfway through a commercetools build were asking strangers what to do next, and Dunphy says the story is fiction. I have not seen the thread, so I am leaving the question of who wrote the story to him.

I spent four days writing, rewriting, analyzing, and scrutinizing this issue. I removed 95% of the Claude content and left the analysis. (I am fully transparent in the article and you can skip the Claude shit if you want)

A bot that posts on Reddit does not deserve the credit ChatGPT gives it, and the evidence is below.

Why Reddit Threads Land in AI Answers About Platforms

Note to the reader: This is lots of research done by Claude. I wouldn't blame you if you just skipped this section.

In the next section, Claude will tell you a ton of stats and lots of bloated info that you may or may not want to read about ChatGPT and Reddit. I have so many sources to reference at the end, it is quite impressive. I will bold the sections I wrote. You can read the bold bits and get most of this section.

Take it away Claude! yippeee

Reddit is one of the sources AI engines cite most. Profound, an AI visibility company, counted 680 million citations between August 2024 and June 2025 and found Reddit at 21.0 percent of Google AI Overviews' top ten sources, 11.3 percent of ChatGPT's, and 46.7 percent of Perplexity's.

Semrush watched ChatGPT cite Reddit in about 60 percent of prompt responses in early August 2025 and about 10 percent by mid-September, which tells you the engines are still deciding how much weight a thread deserves.

People who watch Reddit know what is coming. MediaPost reported in July that AI-generated fake posts are being written to land in ChatGPT and Google AI results, and MediaPost cited Reddit's own figures of 23 million spam views blocked per day and nearly 2 million inauthentic votes revoked per day.

Researchers at the University of Zurich posted more than 1,700 AI-written comments under invented personas to a Reddit forum in 2025, and Reddit's chief legal officer called the experiment "deeply wrong on both a moral and legal level." That was a university experiment with no vendor involved, and I cite the experiment because AI-written posts under invented personas on Reddit are not hypothetical.

Planted text does not need to be large to matter. Anthropic, the UK AI Security Institute, and the Alan Turing Institute found that 250 malicious documents were enough to backdoor language models from 600 million to 13 billion parameters, which is 0.00016 percent of the training tokens, and the researchers say it is unclear whether the result holds for behaviors more complex than the gibberish they tested.

That study covers training data, which differs from the live retrieval an AI does when you ask about platforms, so it shows how little planted text can move a model and stops short of explaining any one thread.

AI slop is producing reviews, updates, and messages about all sorts of platforms all over the internet, especially on Reddit.

Now back to our regularly scheduled program

What I Tell People to Do About It

I would encourage people to filter down and do the research you need to do. If you see someone saying I was about to close on platform X, and I decided on Y then do your due diligence.

I also believe that the AI Slop-Shit that is happening, especially on Reddit, is going to get downgraded. It has to for our own sanity.

Until the Reddit slop gets downgraded, here are ten questions to ask your SI, the system integrator who will build the store, and this list is not comprehensive, only a place to start.

I wrote the questions for the SI and not for the platform because the platform answers most of what you need in public, in its documentation, its release notes, and its customer list, while the SI holds the answers that only come out in a conversation, who will do the work, what the work costs, who carries the overrun, and what happens when the scope changes, and the story that started this issue was a build paused because the complexity came in higher than scoped.

  1. Which projects like mine have you delivered on this platform, and will the clients take a reference call?
  2. Who will work on my project, at what seniority, and how long has each person worked on this platform?
  3. Is the pricing outcome based or time and materials, and who carries the overrun if complexity comes in higher than scoped?
  4. Which parts of the build come out of the box, and which parts are custom code I will maintain?
  5. Which features in the plan are generally available, and which does the vendor's own documentation call beta or experimental?
  6. What does year two cost, including maintenance, upgrades, and licenses?
  7. Which other platforms in my vertical did you consider for me, and why did you rule out the other platforms?
  8. How do you handle change management when the scope changes, including who approves the change and how the change is priced?
  9. Who owns the code, the data, and the accounts when the project ends, and how does my data get used if the platform is SaaS?
  10. Can I run a proof of concept on my own catalog and one checkout path before I sign?

Footnote. Claude helped me come up with these ten questions, and I encourage you to comment with better questions.

You don't want to check just one platform. You want to check other platforms that are in the same vertical as the platform you are interested in. For example, if it's commercetools, you want to look at other headless, API-first platforms that make sense for you to consider. There will always be four to five platforms that fit your vertical.

Examples of Checking Your Platform

I did the slow version on two platforms I like. On the Shopware side, the press release promised "100% MCP coverage," while the developer docs say the MCP server is experimental and still in beta. A merchant reading only the press release would come away with a different picture than a merchant reading the docs. The open issues tell a good story too. A bug that broke the extension's install was fixed within two days, with an apology.

On the commercetools side, the MosAIc page describes a preview and asks visitors to express interest, and the AI Hub page lists customer logos without saying which of them run on agentic channels. Which of those customers run on agentic channels is a question for a reference call, and a fake thread has no reference to call.

The list of platforms is much larger, but I chose these five, and I think all five are winners, commercetools, Shopware, SCAYLE, BigCommerce, and of course the elephant in the room, Shopify. A thread that nobody can trace to a person helps none of them. (I don't believe Shopify is the best solution for B2B, but I'd love to hear why others think it is.)

Three of these platforms, commercetools, Shopware, and SCAYLE, are German, and SCAYLE is just getting its footing in the US. Shopware is primarily B2B. So I have a question for you, are these Reddit posts geared to the German market?

Platforms I Have Already Written About

I chose Shopware and SCAYLE because Dunphy's post mentioned them, and I added BigCommerce and Shopify because I wanted a few more. I can only name a few platforms in one issue, and I apologize to the ones I leave out. Here are a few I have already written about, so you have my research to start from.

To restate my premise, when an AI names a platform to buy, what do you check before you trust the answer? Do you look at the citations? Or do you read what people are saying on Reddit?

Claude did the research and drafted the first pass of this issue, and I edited before anything went out. Shopware presented issue 63 of this newsletter and has no part in this issue.

More than 95 percent of this issue is in my words, and the remaining 4 percent is sourced facts and links. Claude gathered the Reddit research, and I left the Reddit section as written.


SOURCES

Thursday, December 8, 2022

Multiple-user student webserver

 

For today’s (well, the last week or so) post, I’ll outline the steps needed to build a webserver that can be used by multiple users, authenticated from an active directory environment, to host content stored in individual personal directories. Last month, our existing student webserver (StudentNet) died as it was being hosted  on a disk array that was long overdue for being replaced. After 4 hard disk failures on a RAID 6 enclosure (accounting for the two hot spares that had been setup), it then died taking 10 servers down with it. Hopefully we will be able to purchase a new storage array soon – but in the meantime we need to get StudentNet back up and running for the start of term in October.

What is (or was) StudentNet?

StudentNet was a Ubuntu 12 server that was originally advertised as having the following software installed:

  • Apache
  • PHP
  • MySQL
  • Perl
  • Python
  • Firefox
  • X11
  • Oracle Java 7 (JRE and JDK)

It could be accessed internally on-campus through SSH and CIFS and finally, users could access their own directory via a web-browser by appending the url with their student ID, even externally. For now, I am only focusing on the items in bold.

Setup a basic LAMP server

First things first is to install a new VM on our ESX cluster. I recently updated the local ISOs available to the host machines to include Ubuntu 14 but I suspect that this will likely make no difference than had I stuck with using Ubuntu 12. As I have absolutely no idea of what sort of CPU load to expect, I decided to set the VM as having a single 4-core virtual CPU with 16GB of memory; it can be expanded later in the virtual machine settings. For making a new disk, I selected FFD3 – the new drive array, built from 6 (5 + 1 hot swap) 15K RPM SAS drives, collected up from no-longer (and never particularly often) used servers lying around. Like the existing SAS drives that failed they are old disks still, but most probably saw barely any use in their life (all were pillaged from other Dell servers we had around). Now, FFD3 is going to be exclusively used for studentnet – so I can allocate the entire disk group with thick provisioning to be used by the VM – if the I/O activity shreds these disks to oblivion, nothing else shares them and we can just rebuild the whole server again.

During a standard installation of Ubuntu, you can select a LAMP server as an option, but after selecting only to install OpenSSH and running a package update, I then went ahead to configure it as a LAMP server by manually installing the following:

  • apache2
  • apache2-utils
  • mysql-server
  • php-pear
  • php5
  • php5-mysql
  • php5-gd
  • php5-mcrypt
  • php5-curl
  • libapache2-mod-auth-mysql

The first thing I went ahead and did was to configure MySQL by running mysql_install_db followed by mysql_secure_installation, selecting the options to remove remote login through root and removing the test database.

I then modified /etc/apache2/mods-enabled/dir.conf to push index.php to be the preferred file (So that it now reads as “DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm_”) and did a service apache2 restart.

Finally, to test it all I created a new .php file for testing success under /var/www/html/ that just verifies mysql and php. It works!

LDAP support

I downloaded PowerBroker Identity Services from http://download.beyondtrust.com/PBISO/7.5.1.1517/linux.deb.x64/pbis-open-7.5.1.1517.linux.x86_64.deb.sh and installed it bash. I think it can probably
also be installed by running apt-get install pbis-open but I had installed 7.5 by downloading the package manually already. PBIS replaces Likewise, which used to be in the Ubuntu repository (but apparently now isn’t), and enables users to logon with active directory credentials. This means that students can now, hopefully, log in with their student IDs.

However, to do this, first we need to join the server to Active Directory by using the /opt/pbis/bin/domainjoin-cli command with the format of

“OU=<folder>,DC=<Our university>,DC=ac,DC=uk” ouruni.ac.uk serviceaccountforad

(you don’t need to add the domain to the account name). PBIS prompts you for the password and, after this, the errors started (albeit usually with no descriptions).

Now to change the PBIS configuration defaults for users logging on (so, which directory is created for them etc)

 sudo /opt/pbis/bin/lwsm refresh lsass
 sudo /opt/pbis/bin/config AssumeDefaultDomain true
 sudo /opt/pbis/bin/config HomeDirUmask 072
 sudo /opt/pbis/bin/config Local_LoginShellTemplate /bin/bash
 sudo /opt/pbis/bin/config Local_HomeDirTemplate %H/%D/%U
 sudo /opt/pbis/bin/config LoginShellTemplate /bin/bash
 sudo /opt/pbis/bin/config HomeDirTemplate %H/%D/%U
  [which represent Homedirectory/Domainname/Username]
 sudo /opt/pbis/bin/config UserDomainPrefix UOB 
 [Prefixes users and groups with UOB]
 [This restricts who can use the system, but wont use it for now. In future we might.]
 sudo /opt/pbis/bin/config RequireMembershipOf "UOB\\allStudents" "UOB\\CSTLinuxServerAdmins" "UOB\\allStaff"

I then added the Linux Server Admins group to the list of super users by running VISUDO and appending it with this like at the bottom

%CSTLinuxServerAdmins ALL=(ALL) ALL

Now, any user in that group can make system changes. Only a few people should be allowed to do this though! For a bit more security, I edited /etc/login.defs to change UMASK to 072 from 022. This means that now, nobody except people outside the group can read data and nobody except the owner can modify it.

After restarting the server, it is still joined to the domain and I can successfully log in as either a staff member or student!

 

Enabling user directories

So it seems that to get what we want, user-specific web folders, we have to use “userdir”. If I type “a2enmod userdir” to enable the module, and restart the apache2 service, I now should be able to login with WinSCP, right?

Right! I have my own directory specified as /home/UOB/(my username)/, however I can also browse other directories outside of /home/, which needs to be secured sometime. After making the changes below to /etc/apache2/mods-enabled/userdir.conf, users can put all of their files into a folder in their home directory named /public_html/ and access them through a browser at http://<server>.ac.uk/~<username/. Without the following changes, users who try to view the page will get a message that they are forbidden from being able to do so.

<Directory /home/UOB/*/public_html>
   AllowOverride All
   Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
   <Limit GET POST OPTIONS>
      #Pre-Apache 2.4
      #Order allow,deny
      #Allow from all
      Require all granted
   </Limit>
   <LimitExcept GET POST OPTIONS>
      #Pre-Apache 2.4
      #Order deny,allow
      #Deny from all
      Require all denied
   </LimitExcept>
 </Directory>

And with that (on default Ubuntu 14 installations, it is just really two changes as highlighted), we now have a working student webserver!

Next time, I will probably try and get user directories working with Samba in Windows, so that users can drive map to their area on studentnet – but this will likely make them re-use it again as a storage area where they will inevitably run files from again, creating the huge I/O activity we saw before. But we will see how students need and use it over the coming months.

Troubleshooting

  • To test if the server is in AD, I just run “pbis status”. I have found servers that have somehow “lost” their AD binding, you can just then use “/opt/pbis/bin/domainjoin-cli join” to rejoin the domain and restore AD connectivity. You can also check the config file by doing “/opt/pbis/bin/config –dump” and seeing if the settings match up as above. This might also be /opt/likewise/bin/lwconfig instead, for older installations that used Likewise before PB took over.
  • Sometimes the server may not join if the computer has been moved from its original OU to another one. To remedy this, I went into the Active Directory tree structure and deleted the computer account on the domain controller. I then attempted to rejoin the server with a success.

Bootnote: I had originally used “pbis join” as the command to join the domain. However there seems to be actually nowhere on the internet at the time of writing that specifies “pbis join” as being a command to use! It might be possible to use this instead of domainjoin-cli, but there must be something that domainjoin-cli does that pbis join doesn’t.

I suspect this is to do with /etc/pam.d/common-session which pbis join doesn’t alter, but domainjoin-cli does (it adds session sufficient pam_lsass.so and session optional pam_systemd.so to the end of the file). Additionally, the syntax is a little different than if you were to use domainjoin-cli as above (error descriptions are not always forthcoming so from the small amount on the “pbis join” command, I can only assume that this isn’t fully supported by Powerbroker – yet?):

  • First of all, I had Error 40022; I forgot my AD service account password, so I had to reset it.
  • Then I had Error 40320 – LW_ERROR_LDAP_INVALID_DN_SYNTAX – I had the syntax wrong; you don’t need to have the entire OU/DC structure, just the OU as follows: “LowestFolder/NextFolder/Computers/TopOfTree”.
  • This gave me Error 40318 – LW_ERROR_LDAP_NO_SUCH_OBJECT – I had the syntax now the wrong way around; The error indicates that the OU object doesn’t exist as it was written, so it is now  “TopOfTree/Computers/NextFolder/LowestFolder”.

After correct this one last time, it now finally works using pbis join –ou “TopOfTree/Computers/NextFolder/LowestFolder” ouruni.ac.uk serviceaccountforad.

However, this actually didn’t allow me to login, even after rebooting, as any domain user. I could find them wiith “pbis find-user-by-name” and a username of someone I know and it returns a user, telling me that it can locate users. But I can’t log in as one, bizarrely. It was then that I reverted to using domainjoin-cli

Thanks, in part, to http://andys.org.uk/bits/2010/01/28/likewise-open-and-linux/

Wednesday, December 7, 2022

Multiple-user student webserver

 we have had a second catastrophic outage of our storage array which, once again, has taken Studentnet with it. However, going from my previous blog post has allowed me to get this up and running fairly quickly. In the process, I found some addendums that I would make to the original – but rather than edit it in, I thought it would be better to make a new post to explain the differences. Plus, I found a much quicker way to get it up and running..

 

Change 1: Install Linux as a LAMP server

This is really simple. Lots of things will get installed by default here; the pear, php and mysql modules all get installed with this. Selecting this and OpenSSH is all I did this time around. Straight afterward this, all that needs to be done is the original step of altering /etc/apache2/mods-enabled/dir.conf to push index.php to the start as well as an apt-get update and upgrade

Change 2: Powerbroker/Likewise installation

This time around, I used pbis 8.2, rather than 7.5. apt-get install pbis-open doesn’t work – so we need to get it from Beyondtrust’s download site manually. Over at http://download1.beyondtrust.com/Technical-Support/Downloads/PowerBroker-Identity-Services-Open-Edition/?Pass=True can be found the latest version for a given distribution; for Ubuntu, currently, this is currently located at  http://download.beyondtrust.com/PBISO/8.2.1/linux.deb.x64/pbis-open-8.2.1.2979.linux.x86_64.deb.sh which uses the Debian release.

The following commands are how I got this working:

cd /tmp
//Make a temporary directory

wget http://download.beyondtrust.com/PBISO/8.2.1/linux.deb.x64/pbis-open-8.2.1.2979.linux.x86_64.deb.sh
//Download to this directory

sudo chmod +x pbis-open-8.2.1.2979.linux.x86_64.deb.sh
//Give execution permission for this file

sudo ./pbis-open-8.2.1.2979.linux.x86_64.deb.sh
//Run the installer. Dont select legacy links, dont need it, and select yes to install now

sudo domainjoin-cli join --ou "OU=ComputerFolder,DC=company,DC=co,DC=uk" company.co.uk myaccount
//The account "myaccount" has to be able to join the domain. You'll be prompted to enter a password.

After this, the server can now accept domain logins. The only other thing to do now is to add in some AD configuration, for things like the prefix for the domain (so users don’t have to type their username@domain.. they only need to type username) as well as where their home directory is located. The following lines add entries to the PBIS configuration:

sudo /opt/pbis/bin/config UserDomainPrefix MYDOMAIN
sudo /opt/pbis/bin/config AssumeDefaultDomain true
sudo /opt/pbis/bin/config LoginShellTemplate /bin/bash 
sudo /opt/pbis/bin/config HomeDirTemplate %H/%D/%U 
sudo /opt/pbis/bin/config RequireMembershipOf "MYDOMAIN\\students" "MYDOMAIN\\staff" "UOB\\TechStaff"
sudo /opt/pbis/bin/config HomeDirUmask 072

 

Change 3: Quick permissions changes

The only next thing to do now is to ensure that “TechStaff” are able to act as admins. We can do this by adding the following, somewhere in the sudoers file (nano /etc/sudoers):

%TechStaff ALL=(ALL:ALL) ALL

TechStaff users can now do everything. Now, for the web server part, we set home directories to be only changed by their owners (and read by anyone in the group and executed by anyone else) already, but we need to change one small thing in the apache2 user directory config after making sure we have run a2enmod userdir – Inside /etc/apache2/mods-enabled/userdir.conf , the line /home/*/public_html needs to be changed to /home/domain/*/public_html – which is specified in the HomeDirTemplate above (home/domain/user – %H/%D/%D). The next line, AllowOverride, should just say All after it, too.

After restarting apache2, everything should work alright, as before. PBIS was a lot easier to install and join the domain with this time around and with the LAMP installation automated, everything was just a lot quicker to get running.

Tuesday, December 6, 2022

Feeding the frontend – displaying data with D3.js

 

It’s been a while since I’ve posted any updates about Switchy McPortface. It works just fine in my dayjob and the data can be viewed by anyone wanting to see what is plugged into what, which has been helpful in a few situations.

However, aside from a few customised tables and functions, there’s not really a lot more that I’ve done to improve functionality. What I have done, however, is embark on making a nicer frontend with some interaction. Essentially what I am aiming to do is to display all of the computers on a single webpage for a given room, roughly corresponding to the positions that they actually exist in in each room. My first thought a few years ago was to use Unity3d or C++ and OpenGL but, since I’ve already used these things before, why not try something new and use Javascript to make a web app or something?

This guide is going to go through a nicer way to display data than just using a table generated from some basic HTML in php. There’s a few libraries out there, but I thought it would be nice to get stuck into something that has some widespread use and the name Data Driven Documents sums up nicely what I’m trying to do – generate something visual based on some underlying data. D3 gives a nice way to manipulate the DOM, bind data and provide some visualisation and I felt it fit somewhere between jQuery and (other) visualisation libraries for this project. So without further ado, here is a quick rundown of one way to use d3.js to generate something prettier than a few table rows and columns!

Starting out

Because this builds on my previous work (with the desktop client now uploaded to Github), I won’t go into exactly how you get this specific data generated, or a web server set up and running. The focus here is how to interpret data with a basic web page – so hopefully you’ll already have a way to serve and process some php files.

For this project overall, I used three files:

  • index.html – the main file you’ll call when you open your browser. It’ll also contain the page styles, instead of a separate .css file, and invoke the Javascript file
  • main.js – the file that will contain the scripts used that, in turn, call d3.js
  • query.php (this comes in part 2) – the file that handles the communication between your page and a database

The index and js files can sit at the web directory root of the server, or they can reside on your own machine. I’ve been using Brackets and testing this on my local machine, with php files running off a virtual machine and the concept is really simple:

  • Open index.html 
  • Load d3.js and the main.js files
  • Load in some data that represents computers
  • Create an SVG on the main page
  • Create a sub shape for that SVG for each computer

Index.html

The html landing page isn’t going to have an awful lot in it. Its primary purpose is to load the JS files, which in this case are d3.js v4 and jquery (used for simple AJAX calls later in part 2).

<html>
    <head>
        <title>Room test</title>
        <script src="https://d3js.org/d3.v4.js"></script>
        <script src="jquery-3.1.1.js"></script>       
    </head>
    <body>  
    </body>    
        <script src="main.js"></script>
</html>

Note that main.js is called after everything else; this is so that it loads after other JS libraries. You also don’t need to type out the entire <script type=”text/javascript” src=”main.js”></script> as of html5, since Javascript is the default script type now and the “type” attribute can be omitted.

main.js

This is where the main gubbins of this example resides. The gist of it is:

  • Create a blank SVG
  • Load in list of computers and a list of positions
  • Draw a new circle for each computer at its respective position
  • For fun, display the hostname for each of those computers when you hover over it with the mouse

Here’s each part broken down:

Creating a blank SVG

Once you’ve loaded the d3.js library in your index.html file, you can access d3 functions as in the example below. All it does is to create a blank SVG, appended to the body of the page, with an arbitrary width and height.

var w = 500;
var h = 450;
var svg = d3.select("body")
            .append("svg")
            .attr("width", w)
            .attr("height", h);

 

Load in computers and positions

So far so good? Next I’m just going to create two objects for a Computer and a Position, each taking some parameters to represent what they are. A computer is, for now, defined as simply a name – and this has a “place”. A position is a combination of a place number and its respective x and y coordinates. The reason these are separate is because at some point we might have different rooms or position layouts and I want to keep the computer and position data separate.

function Computer(place, hostname) {
    this.place = place;
    this.hostname = hostname;
}

function Position(place, posx, posy) {
    this.place = place;
    this.posx = posx;
    this.posy = posy;
}

var positions = [
    new Position('10', 0, 0),
    new Position('20', 80, 0),
    new Position('30', 160, 0),
    new Position('40', 240, 0),
    new Position('50', 0, 100),
    new Position('60', 80, 100),
    new Position('70', 160, 100),
    new Position('80', 240, 100)
];

var computers = [
    new Computer('10', "WS10562"),
    new Computer('20', "WS10239"),
    new Computer('50', "WS10555"),
    new Computer('60', "WS9111"),
    new Computer('70', "WS11032"),
    new Computer('40', "WS11031")
];

So here are two arrays of data for the computers and positions. The logic is that, whilst there may be any number of positions, they may not all be filled by a computer. Anyhow, this is all sort of irrelevant to D3 for now (and it could have been loaded in externally), so I’ll get on and demonstrate how you can now put something on screen.

Draw a new circle for each computer at its respective position

Very simply, I’m going through that array of computers and, for each one, I’ll add a circle at that position.

for (var x = 0; x < computers.length; x += 1) {
    
    var posIndex = positions.findIndex(y => y.place == computers[x].place);
                                        
    svg.append("svg")
        .append("circle")
        .attr("cx", positions[posIndex].posx + 30)
        .attr("cy", positions[posIndex].posy + 30)
        .attr("r", 20)
        .style("fill", "purple");

}

To match the data in the positions array up with the computers array, I need to first find the index of the correct item in positions array that corresponds with the “place” of the computer at the current index. In SQL, a left or an inner join would do what we need to do but in this example, I’m using two separate arrays of data that I need to match up.

Apparently, as of ES6, you can use findIndex. What this will do is return the index of positions where it finds a match by the function provided. Because I’m trying to match a property of an item in the array, the function needs to compare that “place” property for each item in the position array to the current computer’s “place” property. The => operator shortens the need to make that function by using y as the variable to represent the operative array item and it will return true when it is equal to the condition given.

For each of these computers, you can then append the SVG created earlier with a circle and give it the attributes for the radius, x and y coordinates (with an offset) and modify their style (which could be done in the index.html file but you can do it now too), which is just the fill colour here. This just adds some circles nested within the SVG tag that has been added to the page – nothing hugely complex, which is the great thing about D3. It is just a nice way to access the DOM and to add elements to a webpage dynamically.

It is important to note that this is not the best way to use D3. The way in which it should be done is to say you’ll add all of the elements of a given shape in one call then pass the data in – here, we’re going through the data first and then just adding one element on each iteration of the loop. There is no need to use a for loop in d3 and so what I’ve done is counter-intuitive to the way you’d normally learn it; but I’m simplifying the process of combining two arrays’ worth of data which I haven’t been able to find a nicer way to do when using D3. Besides, later it won’t be necessary; however, it was useful to figure out a nice way to make things work for now.

Display the hostname on mouse events

This is really easy to do. You first need to modify the previous code a little to look like this:

    svg.append("svg")
        .append("circle")
        .data(computers) //Add this!
        .attr("cx", positions[posIndex].posx + 30)
        .attr("cy", positions[posIndex].posy + 30)
        .attr("r", 20)
        .style("fill", "purple")
        .on("mouseover", fadein) //Mouse over event
        .on("mouseout", fadeout); //Mouse moved away event

Although it isn’t used as extensively as it will be later, the data function has been added in which is needed to provide the hostnames to each of the shapes when you mouse over them. For that to happen, two events need to be added with the on function – which are “mouseover” and “mouseout” (events that are triggered when the mouse is detected to have entered and exited the boundaries of the element in question). As much as I like the whole anonymous function thing in JS, I’m just going to call some named ones because I hate polluting what should be simple code with a bunch of ugly long functions that are more than 2 or 3 lines long.

And these are the functions you need:

var div = d3.select("body")
            .append("div")
            .attr("class", "tooltip")
            .style("opacity", 0)
            .text("");



function fadein(d, i) {
    div.transition().duration(200).style("opacity", 0.9);
    div.style("left", d3.mouse(this)[0])
	.style("top", d3.mouse(this)[1])
	.html(d.hostname);
    //console.log("fadeoin");
}

function fadeout() {
    div.transition().duration(400).style("opacity", 0);
    console.log("fadeout");
}

Here, the two functions can take two parameters passed into them by d3, which are the data and an index.The data is the element of the array used when each shape is created (although, in this case, that will always just be the first element, since only one circle is added at a time). The index will be which iteration that d3 element was created during.

I’ve also added a new div to the page up there too – that’s because, in order to have text pop up, I want to do it in a tiny floating “box”, which is really just an HTML element. As a result, changing the text is as simple as changing the html property of the object to whatever the hostname of that data object is. Note that, if you were to use a totally different dataset for this, “hostname” would have to be replaced by whatever else you would want to have displayed instead. I also have it appear at wherever the mouse is with a bit of a transition time, because that makes it look a bit swishier.

One last thing is that, to make it actually look nice, you might want to add a style just for the tooltip (hence div.tooltip) to the main html file within the <head> section somewhere:

<style>          
div.tooltip {
                position: absolute;
                text-align: center;
                width: 60px;
                height: 29px;
                padding: 2px;
                font: 12px sans-serif;
                background: green;
                border: 0px;
                border-radius: 9px;
                pointer-events: none;
		color: white;
            }
</style>

This centers the text and gives it a solid colour background with a bit of a rounded border and some padding.

Conclusion

If you’ve done it right, the result should look something like this (I’ve added both circles and rectangles here as a test and reduced the number of “computers” a bit for this example)

 

Carrying straight on from my previous post on the subject, I’m going to go through the alluded to third page to add, which is query.php and change main.js a little, too. The purpose is to swap out the hard coded arrays of data for an external data source, namely a web-based resource rather than any locally stored files using AJAX and JSON.

Query.php

What we’ve got so far are some blobs being drawn at various positions on the screen, based on the data stored in a couple of arrays. It isn’t all that exciting, but the main thing is that we have data in an array that can be visualised. The next step is then to load in that list of a computers from an external source. D3 can do things like load CSVs and JSON data in from a file, but since I’ve been using php to fetch data already from my database, I felt that it would be worth just adding a bit more code to what is already there to put that data into d3.

In another previous post I’d displayed the results of a query in a table, but what I want to do instead is to store them in JSON format. Its a data format that can be read and used by many different parsers on different platforms as well as being able to be directly read in by Javascript and interpreted as a list of objects.

Below is what you should be able to use to return and display a JSON string.

<?php 
	
	$username = "user";
	$password = "password";
	$room = $_GET['room'];

	$serverDB = "mysql:host=localhost;dbname=inventory";
	$conn = new PDO($serverDB, $username, $password);

	if ($conn->connect_error) {
		die("Connection failed: " . $conn->connect_error);
	}
		
	$getHosts = "SELECT * from hosts WHERE Room = '". $room ."'";

	$hosts = array();
	
	if ($result = $conn->query($getHostsPlaces))
	{
	while ($row = $result->fetch(PDO::FETCH_ASSOC))
		{
			$hosts[] = $row;
		}
		
	echo json_encode($hosts);
	}
	
	$result->closeCursor();
	$conn = null;

The first thing to note here is that, in this example, the request for the page will take the form of http://url/query.php?room=A001 – where “A001” would be the room you are asking to have the computers returned from. This lets me create the select statement with the room specified and return only the computers in a certain room.

I then create an array that has a new element added for each row (leftover terminology from the previous example). The syntax isn’t obvious but assinging a value to an array without specifying a key seems to be the same as array.push() in Javascript. After that, I then call json_encode and pass it the array of hosts, which serialises the data to be read in by something else later. In this instance I simply echo the data, which should be an array of data.

One last thing is that, unlike any previous examples, I try to now use PDOs. Included with most php/mysql installations of Linux, its a technology-neutral way to access most databases (although the connection string does specify that it is a MYSQL database), so in lieu of any real reason not to, I have decided to go down that route. However, one important thing I found out was that, if you don’t properly close the connection and queries when you’re done, you get internal server errors. So, whilst you didn’t have to do this with mysql(i) connections, you absolutely do have to with PDOs, which is probably a good thing.

When I call this page from my browser, what is displayed is the JSON’d data, as expected:


With that done, I can now go back to my main.js file and make some changes so that this data is read in.

main.js

jQuery and d3.json

It is at this point that the reference to the jQuery library becomes relevant. To access the jQuery object, you just have to use a $. It turns out that this is actually a legitimate variable name – but thankfully nobody else would be crazy enough to use it on its own so jQuery can use it all on its own. What it can then be used to do is to manipulate the DOM, like D3, although in a different way. But it can also be used to perform an AJAX request – used to grab data from another page via an XML HTTP Request, but with a fraction of the code. To call our query.php page and interpret the returned data as JSON, you can use the following code below.

$.ajax({
	url: "query.php?room=w004",
	dataType: "JSON",
	success: makeComputers
});

The callback function is what is invoked (with the data passed in as a parameter) when the AJAX call is successful (you can create a similar callback for a failure, too). I’ve made a function called makeComputers, where I will put all of the previous code.

Update: I have since found that there is an even simpler way, when using d3.xhr or d3.json. For this example, you can reduce the above code down to a single line and cut jQuery out completely:

d3.json("query.php?room=w004", makeComputers);

In either case, the result will be the same; makeComputers will be called when the call has completed. This is an asynchronous call, though, so any code inside makeComputers will likely happen a few milliseconds after whatever follows either call.

I’ve modified the original code from last time to be as follows:

function Computer(place, hostname) {
    this.place = place;
    this.hostname = hostname;
}
function Position(place, posx, posy) {
    this.place = place;
    this.posx = posx;
    this.posy = posy;
}

var positions = [
    new Position('1', 0, 0),
    new Position('2', 80, 0),
    new Position('3', 160, 0),
    new Position('4', 240, 0),
    new Position('5', 320, 0),
    new Position('6', 400, 0),
    new Position('7', 0, 100),
    new Position('8', 80, 100),
    new Position('9', 160, 100),
    new Position('10', 240, 100),
    new Position('11', 320, 100),
    new Position('12', 400, 100),
    new Position('13', 0, 200),
    new Position('14', 80, 200),
    new Position('15', 160, 200),
    new Position('16', 240, 200),
    new Position('17', 320, 200),
    new Position('18', 400, 200),
    new Position('19', 0, 300),
    new Position('20', 80, 300),
    new Position('21', 160, 300),
    new Position('22', 240, 300),
    new Position('23', 320, 300),
    new Position('24', 400, 300),
];

var computers = [];

var w = 450;
var h = 450;
var svge = d3.select("body")
            .append("svg")
            .attr("width", w)
            .attr("height", h);
var div = d3.select("body")
            .append("div")
            .attr("class", "tooltip")
            .style("opacity", 0)
            .text("Tooltip");

function makeComputers(jsony){
	for(var k=0; k<jsony.length; k++){
		computers.push(new Computer(k+1,jsony[k]['hostname']));
	}
	var recties = svge.selectAll("rect")
			.data(computers)
			.enter()
			.append("svg")
			.attr("data-hello", function (d,i) {return d[i]; })
			.append("rect")
			.attr("width", 30)
			.attr("height", 30)
			.attr("x", function(d,i) {var xloc = positions.findIndex(y => y.place == computers[i].place); return positions[xloc].posx +15})
			.attr("rx", 6)
			.attr("ry", 6)
			.attr("y", function(d,i) {var xloc = positions.findIndex(y => y.place == computers[i].place); return positions[xloc].posy +15})
			.style("fill", "Lavender")
			.on("mouseover", fadein)
			.on("mouseout", fadeout)
			.on("mousemove", moviemouse);
}
function fadein(d, i) {
    div.transition().duration(200).style("opacity", 0.9);
    div.style("left", d3.mouse(this)[0])
	.style("top", d3.mouse(this)[1])
	.html(d.hostname);
}
function fadeout(e) {
    div.transition().duration(400).style("opacity", 0);
}
function moviemouse(e) {
    div.style("left", d3.mouse(this)[0])
        .style("top", d3.mouse(this)[1]);
}

d3.json("query.php?room=w004", makeComputers);

Just to break this down in summary:

  • The makeComputers function starts off by adding all of the computers imported to the computers array based on their hostname
  • It then adds in rectangles, with the mouse listeners, using the better D3 method of adding SVGs to the page.
  • I’ve also used the findIndex function without needing a for loop, since D3 provides the index to be able to do this.
  • There are no longer any computers specified in the array of computers
  • There are now three functions associated with fading in/out and movement of the mouse (so the position of the tooltip box will change as the mouse moves)
  • Finally, this all happens when I call makeComputers at the end as the function that is executed once the request to the given URL has completed

The only last modification you may need to make, if your query.php file is stored on a different server to the one you are running your index.html and main.js from, is to add this to the top of your query.php file:

<?php 
 header("Access-Control-Allow-Origin: *");

This allows calls from other domains to be made, which is disabled by default for security reasons. However, if you’re confident that (for testing purposes at least) this won’t be an issue, then you can go ahead and enable to it, which lets you do things like run your index.html page and JS files from your local disk and make queries to the remote server.

This should have hopefully got you to a stage where you can make requests for JSON data to a web server and have it return some SQL results as readable data by JavaScript, but if anyone has any issues or encounters any oddities, do get in touch!

 

Ok so this is a bolt-on post to my previous post, where I am now essentially trying to figure out a nice way to map the data between the computers and their positions in a room. The way I’ve designed the system is that I have three tables with the relevant data:

  • Hosts: The main piece of data here is the hostname and other data – but it also contains the room that the host belongs to (and the “place”, which is just a notional number that I placed on a diagram)
  • Rooms: This is the list of rooms and their “layout type”. We could have ten different rooms each with the same layout, so this is a way to say which room has which layout type. Its sole purpose is to join the other two tables.
  • Places: The combination of a layout type and a position will give you an x and a y coordinate. This is a relative coordinate. It doesnt have to know which rooms are associated with it, its purely data on where – for a given layout type – a “position” would exist.

The idea for this is that you would fetch the respective coordinates for a host from the places table. The hosts table only has the place, so its necessary to fetch the correct layout type that matches the room, and then fetch the coordinates based on that data.

Complicated? A little. Especially for something that doesn’t seem too big. But I want it to be scalable and to separate out the data for positions from the hosts; a room may entirely change its layout but, so long as the room doesn’t change, the only thing I would need to change is the layout type and then the coordinates. However, I could have just placed all of this data into the hosts table – or removed the third table for places and just have mapped each room directly to sets of coordinates (and I still may!).

But I did find a solution, although it took a few iterations. I needed to refresh myself on inner and left joins a bit but my original plan was to do a left join between the hosts table and places table where the hosts’ room is a specific room. But then the places table works on layout type and noot a room number.

Ok so the first step would be something like this:

SELECT hostname, place, room, posx, posy
FROM hosts
INNER JOIN places ON places.position = hosts.place 

WHERE room = "W004"

Select the hostname from hosts, the coordinates from places, the room and the place from the inner join’d josts and places. The inner join will be done where the position field from places matches the place field from hosts. To limit it, I then just put “where room = w004”.

This would return a lot of results, since there’s many hosts potentially with the same place and many places with the same position. The “where” limitation narrows the hosts down to only the selection relevant to the one room, but it still leaves lots of entries potentially from the position, since there will likely be n times as many results as there are positions and layouts. That would then give duplicate host results, a duplicate for each recurring position that appears in the places table.

So the next step is to narrow this further, which was causing me a lot of headaches. I had an idea to expand the “WHERE” statement to include the room, but that isn’t a part of the dataset constructed by the joins. The solution was to do two joins – the second join being where the layout type matches the room specified in the room table and where the room matches in both the room and hosts table. This narrows both the hosts and rooms down to just one room, which means in turn there will only be one layout – and this further restricts the results from places down to what I need.

SELECT hostname, place, rooms.room, posx, posy 
FROM hosts 
INNER JOIN places ON places.position = hosts.place
INNER JOIN rooms ON rooms.room = hosts.room AND rooms.layoutType = places.layout 

WHERE rooms.room = "W004"

Note that I could have used left joins, which would preserve all the host data, but I am trying to narrow it down and there’s just no need for anything else. I should probably also tidy up some of the names a bit, especially since there is now ambiguity between the different “room”s, but not between place and position.

With that done, I can now replace my original SQL statement:

$getHosts = "SELECT * from hosts WHERE Room = '". $room ."'";

With the following:

$getHosts = "SELECT hostname, place, rooms.room, posx, posy FROM hosts INNER JOIN places ON hosts.place = places.position INNER JOIN rooms ON rooms.room = hosts.room AND rooms.layoutType = places.layout WHERE rooms.room = '". $room ."'";

A lot longer, but it returns a single dataset with all the position data we need for a given room!

Sunday, September 10, 2017

Building a Community with Discussion Forums

 

So you’ve tried every tool out there to promote your site and achieved decent results. You’re probably thinking, “Now what?” I have one word for you — “Community.”

Yes, creating a community on your site is the next big step to making your site successful; but, before I tell you how to go about this, let me tell you that building a community is not going to be an easy task. It requires time and patience. But don’t worry, it will pay off.

What I’m going to teach you is how to use discussion forums (also known as ‘message boards’ or ‘bulletin boards’) to get repeat visitors to your site and to build your reputation.

1) How do I install a discussion forum on my site?

First, you will have to choose which program you are going to use. Don’t underestimate the importance of the software being used, as it can turn off many potential ‘netizens’ of your community. Below I’ve listed some of the most popular programs available, and the pros and cons of each.

Ultimate Bulletin Board:

+ Plenty of features.
+ Clean and intuitive user interface.
+ Static pages, meaning less server load.
+ Easy to install and maintain.
– Free upgrades are available only for one year.
– Many people won’t be able to afford it.

My rating (from 1 to 10): 9 Available at http://www.ultimatebb.com

BigTalker:

+ Speed improvements.
+ Compact layout.
+ Clean code, easy to modify.
+ Built-in ICQ feature.
– Dynamically generated pages
– extra server load.
– Less features when compared to UBB.

My rating (from 1 to 10): 8 Available at http://www.bigtalker.com

UltraBoard:

+ Many features (including stats).
+ Price tag (free).
+ Very customizable without having to edit the code.
– Dynamically generated pages.
– Layout may confuse ‘newbies.’
– Grammar errors throughout the program.

My rating (from 1 to 10): 8 Available at http://www.ultrascripts.com

2) How do I get people to start talking?

This is one of the most difficult parts in the process of building a community. People won’t start talking unless you give them a reason. Get several friends to create discussions, always participate in them, ask for moderators and opinions on how to improve the forums. Also, be sure to include a link to your forums on every page of your website. It also helps to run a contest where users can get prizes for their help (ask for a logo, a good name, forum ideas, etc.) or a simple poll regarding a current (and, if possible, controversial) topic.

3) How do I keep the forums active?

Once your forums are active (say, over 100 posts per day), you are not going to need to do much. Be sure to keep SPAM out of your forums by immediately deleting any blatant advertisements. This is the job of the forum moderators, who should be carefully selected based upon their knowledge of the topic and the time they are willing to spend moderating a forum.

4) Facts and tips.
  • Do not expect to make a lot of money using banner advertisements in your forums. The click-through ratios will be too low to justify the money advertisers spend. Don’t bother with click-through networks either, you’ll make a pittance. In case you still want to put advertisements in the forums, be sure they are located at the bottom of each page to increase the click-through ratio; since users are more likely to click on them after they have read the conversation.
  • Try to ask for as much feedback as you can. Visitors love to participate in the building process of the community.
  • Don’t hesitate to delete (or edit) posts that contain either blatant advertisements or bad language. Not doing so will generate fights that might turn off some of your most active forum members.

That’s all for now. If you followed all the tips here, you will be on a route that will build a good reputation for yourself on the Internet. It will also act as a “lead generator” for your site, as people who participate in your forums, and will come to know and trust you, will be more likely to buy your products or hire you.

Sunday, July 22, 2012

It’s Time To Stop Blaming Internet Explorer

Articles like this frustrate me a lot. For most of my career, I’ve fought hard against the “woe is me” attitude embraced by so many in Web development and articulated in the article. This attitude is completely counterproductive and frequently inaccurately described. Everyone was complaining when Internet Explorer 6 had a 90%+ marketshare. That share has shrunk to 6.3% today globally (though Louis cites 0.66%, which is true in the United States). Microsoft even kicked off a campaign to encourage people to upgrade.
I can understand complaining about Internet Explorer 6 and even 7. We had them for a long time, they were a source of frustration, and I get that. I would still never let anyone that I worked with get too buried in complaining about them. If it’s our job to support those browsers then that’s just part of our job. The truth is that every job has some part of it that sucks. Even at my favorite job, as front end lead on the Yahoo homepage, there were still parts of my job that sucked. You just need to focus on the good parts so you can tolerate the bad ones. Welcome to life.
But then the article goes on to bemoan the fact that so many people use Internet Explorer 8 and that Internet Explorer 9 is gaining market share. First and foremost, I would much rather support Internet Explorer 8 then I would 6 and 7. Microsoft forcing most people to upgrade from 6 and 7 to 8 is an incredible move and undoubtedly a blessing.

Internet Explorer 9

Internet Explorer 9, on the other hand, is a damn good browser. The only reason it doesn’t have all of the features as Chrome and Firefox is because they rebuilt the thing from scratch so that adding more features in the future would be easier. Let me say that again: they rebuilt the browser from scratch. They necessarily had to decide what were the most important features to get in so that they could release something and start getting people to upgrade from version 8. If they had waited for feature parity with Chrome or Firefox, we probably still wouldn’t have Internet Explorer 9.
The constant drumming of “Internet Explorer X is the new Internet Explorer 6″ is getting very old. Microsoft has done a lot to try to correct their past transgressions, and it seems like there are still too many people who aren’t willing to let go of old grudges. There will always be a browser that lags behind others. First it was Mosaic that was lagging behind Netscape. Then it was Netscape lagging behind Internet Explorer. Then it was Internet Explorer lagging behind Firefox. People are already starting to complain about Android 2.x browsers.
What makes the Web beautiful is precisely that there are multiple browsers and, if you build things correctly, your sites and applications work in them all. They might not necessarily work exactly the same in them all, but they should still be able to work. There is absolutely nothing preventing you from using new features in your Web applications, that’s what progressive enhancement is all about. No one is saying you can’t use RGBA. No one is holding a gun to your head and saying don’t use CSS animations. As an engineer on the Web application you get to make decisions every single day.

Progressive Enhancement

Louis briefly mentions progressive enhancement as a concept that doesn’t even enter into the equation. Once again, this is indicative of an old attitude of Web development that is counterproductive and ultimately lacking in creativity. The reason that I still give talks about progressive enhancement is because it allows you to give the best experience possible to users based on the browser’s capabilities. That’s the way the Web was meant to work. I’ve included a video of that talk below in case you haven’t seen it.
It’s not actually old browsers that are holding back the web, it’s old ways of thinking about the Web that are holding back the Web. Fixating on circumstances that you can’t change isn’t a recipe for success. The number of browsers we have to support, even “old browsers”, just represent constraints to the problems that we have to solve. It is from within constraints that creativity is born. The Web development community has evolved enough that we should stop pointing fingers at Internet Explorer and start taking responsibility for how we do our jobs. Let’s create solutions rather than continually pointing fingers. We are better than that.
Yes, complaining is useful to get people to listen. Microsoft is listening, so continuing to complain doesn’t do anything except perpetuate an attitude that I would rather not have in Web development. Let’s give them a chance to right the ship without retrying them for past transgressions perpetually.

Friday, December 9, 2011

Clear Indications That It’s Time To Redesign

Redesign. The word itself can send shudders down the spines of any Web designer and developer. For many designers and website owners, the imminent onslaught of endless review cycles, coupled with an infinite number of “stakeholders” and their inevitable “opinions,” would drive them to shave their heads with a cheese grater if given a choice between the two. Despite these realities, redesigns are a fact of any online property’s life cycle. Here are five key indications that it’s time to redesign your website and of how extensive that redesign needs to be.

Metrics Are Down

The first and most important indicator that your website is in need of a rethink is metrics that are beginning to tank. There certainly could be other reasons for this symptom (such as your product not fitting the market), but once those are eliminated or mitigated, a constant downward trend in conversions, sales, engagement activities and general user participation indicates that the efficacy of your current design has worn off. Many people call it “creative fatigue,” but what this really indicates is a disconnect with your audience. The key to solving this in the redesign is to figure out where in the workflow the design is breaking down and then address those areas as top priorities.
Metrics
The metrics are the most important indicator.
The extent to which you redesign to solve sagging metrics could be limited either to adjusting your conversion funnel, if that’s where the problem resides, or to optimizing the product’s main workflow. It does not necessarily mean having to rethink the entire face that your product presents to the world.

Your Users Tell You It’s Time

Metrics give you immediate insight that something is wrong, but to get to the core of what needs to be addressed in the redesign you need to speak with your customers. Surveys work well, but usability testing is most effective. The fluidity of face-to-face conversation allows you to explore the dynamic threads that surveys restrict. If through these conversations you notice consistent patterns that shed light on the drivers behind your downward-trending metrics (and you will), then it’s time to redesign. In addition, these user conversations will reveal prevalent attitudes towards your brand, which can also be addressed in the redesign. In some instances, negative brand perception should be enough to trigger a redesign — but you’d never know about it unless you talk to your customers.

The final decisions are still up to you. (Image: Kristian Bjornard)
Customer feedback will tell you not only whether to rethink parts of your website, but to what extent. Typically, customer conversations focus on specific elements of your workflow. Those areas are the ones that the redesign should focus on. In most cases, this wouldn’t be the whole website, but if the feedback is broad and far-reaching, then tackling the entire experience may be a priority.

The Tech/UX “Debt” List Is Longer Than Your Forearm

Over the course of building a product or website, an organization begins to accrue tech and UX debt. This debt is made up of all the things you should have done during the initial build but either didn’t get around to or had to cut corners on in order to ship the product on time. Each subsequent iteration inevitably adds more debt to the list, until the list becomes so long that it is almost insurmountable. While there are many ways to tackle tech and UX debt on an incremental level, there comes a point when the website, in essence, becomes “totalled.” Like a car that has sustained damage greater in cost than its value, your website gets to the point where starting over would be cheaper than fixing all of the items on your debt list. This is a perfect time for a redesign.
When the debt list gets this long, taking on “incremental redesigns” is easy, where you knock off bits from the list but not the majority of it. This turns into death by a thousand paper cuts, because as you fix elements on the list, you start to accrue more debt around other features. If the list truly is longer than your forearm, then rethink the website if possible.

It Just “Looks” Old

The website’s aesthetic reflects directly on the perception and trustworthiness of your brand. Even if your design was the hotness when it first launched, aesthetics evolve. An old design will be detrimental to your product, leading to the declining metrics mentioned earlier. How can you tell whether your website’s aesthetic is outdated? Look at your competition. Look at hyped-up newly launched services in other sectors. Compare your aesthetics to those of brands that are performing well. Those factors provide excellent barometers by which to assess the currency of your design. The challenge is to review these other websites objectively. Living with your website day in and day out can amplify the feeling that it’s stale and old. Ensure that your assessment is accurate by reviewing your findings with a cross-section of employees in your company.
Win some, Lose some.
Decide on what to lose and what to add. (Image: Kristian Bjornard)
In this case, the redesign would essentially be a facelift, a superficial upgrade of the presentation layer that doesn’t necessarily address the fundamental workflow or conversion funnel — although those aspects will undoubtedly be affected by this aesthetic upgrade.

It’s Been More Than 12 Months Since Your Last Refresh

Even if none of the above indicators apply to your website, the shelf life of an aesthetic in today’s highly iterative online reality is hardly ever more than 12 months. If it’s been a year or longer since you last redesigned your website, then it’s time to redesign. Not only will it refresh the experience for your loyal customers, it will attract new ones. In addition, it will breathe life into the brand and show your user base, the press, your investors and staff that you’re committed to keeping the experience fresh and top of mind.
Again, the focus here is on an aesthetic improvement that keeps the brand current, not necessarily an overhaul.

In Conclusion

These are five simple indicators that it’s likely time to redesign your website, but the list is certainly not exhaustive. The number of them that apply to your situation will determine whether a redesign is imperative. But each indicator on its own is still a strong reason to kick off the next phase of your website’s life. Maintaining a current and fresh face for the online world will yield dividends in customer acquisition, conversion and retention. Also, your staff will stay immersed in the latest technologies, design trends and presentation-layer wizardry if they know that they’ll soon get to exercise their chops in a redesign.
What indicators have you found work best in your organization to drive a website redesign?

Saturday, October 22, 2011

4 People You Should Ask For Website Feedback



Most people know that good feedback is essential to designing and developing quality websites.
But what constitutes “feedback” can be ambiguous: for some, it is little more than a hasty spell-check; for others, it is akin to submitting and defending a PhD dissertation.
While there is no one-size-fits-all solution for those seeking feedback on their work, there are some proven ways to get helpful input from others.
Here are a few ideas and tools to assist you in your quest for an improved product.
Have more ideas? Please share them in the comments area.

1. Ask Your Mom

I’m serious. Better yet, ask your grandma. Heck, even your plumber would be great! The reason I say this is because you must have people review your website who aren’t part of the web design and development community. Too often, web designers design for their industry colleagues and not for the audiences that their clients are trying to reach.
If you really want quality feedback, go a step further. For every project, try to get feedback from actual members of the audience you are trying to reach. If you are building a website for engineers, talk to engineers. If you are designing for a school, talk to teachers and students.
Admittedly, getting actionable feedback from outside the web design community can be hard (see “How a Web Design Goes Straight to Hell“), but at the very least, these groups might alert you to mistakes you’ve made from misunderstanding the target audience.
A lot is to be said for thinking on your own about an audience’s online habits and aesthetic preferences. But as fruitful as that can be, do it only after you’ve stepped out of your ivory tower and mingled with the Internet’s common folk.

2. Ask a Big Name

Here’s a question: if you could get feedback from anyone in the world, who would it be?
Every person would choose someone different, but I’ll bet almost everyone could whip up a shortlist of people who they would love to bounce a few questions off of. Here’s the more important question: what’s stopping you from asking them? Sure, they may be busy and important, but that doesn’t mean trying to reach them isn’t worth a shot.
I don’t meant to brag, but I’ve been able to get just about everyone I’ve approached to reply to an email or talk by phone for five minutes to discuss a few questions. Sure, it took some digging and out-of-the-box thinking on my part (as well as being extra nice to their personal assistants or secretaries!), but it was well worth it. You would be surprised how accessible such people can be if you show genuine (but not creepy) interest and respect for their time.
Tim Ferris has an excellent section in his book The Four-Hour Work Week and in a post on his blog about reaching out to big names to discuss ideas and ask questions. In the section of his book entitled “Find Yoda,” Ferris gives a few tips for getting big name mentors on the phone (the one he mentions from his own experience is author John Grisham).
Not everyone can do this, but that’s the point: only the most innovative, persistent and courageous will follow through on getting feedback from their heroes, and that is ultimately what sets them apart from the crowd.

3. Ask Yourself

Here’s a seemingly obvious one. You are surely already editing your own work and making adjustments as needed. But are you really stepping back and asking yourself deep critical questions about your work (questions that would likely lead to revisions), or are you coasting on what you think looks good at the moment?
Every designer and developer would do well, upon completing a project, to wait before submitting it to the client. Get as far away from it as you can for a day or two, and then come back to it with fresh eyes.
Waiting until that heat of the moment has passed will uncover problems that you missed earlier or perhaps give you new confidence in elements you weren’t sure about before. Either way, taking a step back and asking yourself critical questions will give you a more thoughtful, deliberate piece of work.

4. Ask Your Community

Whether you work alone or with a team of designers and developers, you need feedback from people who are like you and who do the same kind of work you do.
The feedback you get from fellow members of the community is the most actionable because they know the vocabulary and can give you hard, practical advice (instead of the dreaded verdict one gets from clients, “It needs a bit more pizzazz!”). Your community also understands the various factors that affect your work, like budgets, deadlines and stressful clients.
If you are a freelancer or work alone, getting feedback from the wider community can be frustrating, because you don’t have a dedicated team of colleagues who can review what you’re doing. You’re not alone in this (think of the thousands of other freelancers out there), but there are a few ways to work the situation to your advantage.
One way is to trade feedback. Find a web designer or developer whom you respect, and offer to review their next project in exchange for their feedback on yours (you might not even know them personally—but don’t let that stop you!). Figuring out a system for giving and receiving feedback might take a while, but such a relationship has the potential to become beneficial and second-nature for both of you.

Tools for Collecting Feedback

Most websites, with their forums and comment sections, aren’t ideal places to collect and aggregate feedback. Here instead are a few innovative tools that provide great ways to share and receive feedback on projects and designs.
Concept Feedback - Website Review Community
Concept Feedback is a growing community of design, development and marketing professionals and a welcome change to disorganized forum walls. Concept Feedback allows others to provide quick, actionable feedback on your website via a comprehensive rating system, with the requirement that everyone first give quality feedback before receiving feedback from others. Oh, did I mention that it’s free?

Usabilla
Usabilla allows you to collect feedback for any web page, mock-up, sketch or image. Participants (who you are responsible for finding) simply point and click to share their opinions. Paid plans start at $49 per year, but you can start with five pages for free.

Feedback Army
Feedback Army lets you pose specific questions to a panel of reviewers about anything related to your website. This tool is very helpful for seeing the reactions of others to your website’s particular processes (such as placing an order or signing up for an account). Ten different responses will cost you $10. (UserTesting.com provides a similar service.)

Creattica
Creattica brings together a group of top-notch designers looking to share a wide range of design projects. Membership and participation is free, but feedback is limited to labeling items as “favorites.”

Five Second Test
Five Second Test invites random people to look at your website for five seconds and then give feedback on what they remember of the design and other “prominent elements of your user interface.” Probably not the best tool for in-depth analysis of your work, but a great one to gauge first impressions. Free and paid plans (starting at $4) are available.

Notable App
Notable is a collaborative feedback tool for web designers (similar to ConceptShare and ProofHQ). The free plan includes up to three users, but you’re on your own for finding willing subjects.

Please Critique Me
Please Critique Me is where a panel of web design experts picks out websites from among user submissions to give extensive feedback. They can’t provide criticism to everyone, but submitting your work is certainly worth a shot.

Creating Quality Work

A good web design rarely just happens. It is refined over time by having others (and occasionally you yourself) do everything from kicking the tires to taking it for a full test drive.
Putting aside your ego and opening up to criticism might be hard at first, but feedback from others combined with your creativity make for an excellent product that you and your clients will be proud of.

10+ WordPress comment management plugins

Not everyone wants to manage a third-party comment system. And those are the users who will find the comment plugins below really useful.
Below are eleven plugins that can handle everything from adding tweets about your posts to your comments section to stripping out potentially malicious code to preventing spam comments.
There are also plugins that help increase user engagement and usability for your commenters. Let us know in the comments what your favorite comment-related WP plugins are, whether they made the list or not!

1. Twitter Mentions as Comments

As social media continues to grow, a lot of conversations surrounding blog content aren’t happening on the originating blogs. They’re happening on Twitter, Facebook, Google+, and elsewhere. And while it’s great to have people sharing and discussing your content with their social networks, it can sometimes make your blog comments section look anemic.
Twitter Mentions as Comments brings the conversation from Twitter back to your blog. It uses Twitter’s search API to find people linking to your posts and then inserts their tweets alongside your comments (using your blog’s existing comment filters), effectively making Twitter an extension of your blog.


2. WTC Comment Cleaner

This plugin strips potentially malicious code from your blog comments. Just specify what tags you want to allow, and WTC Comment Cleaner will remove all other tags, enhancing security on your blog. It’s currently compatible up through WP 3.2.1.

3. Comment Timeout

Comment Timeout closes comments on posts after a configurable period of time. But in addition to that, you can customize it to allow the open discussion period to extend beyond the default settings if there are recent approved comments on a post, or override comment closing on a post-by-post basis. This way, if there’s still an active discussion surrounding a post, comments can be left open indefinitely.

4. Impostercide

A lot of blogs opt to hold any comments for moderation until a user has had at least one comment approved. And that’s a great idea until some spammer figures out the email address of someone with an approved comment on your blog and overrides your moderation policy by entering an inauthentic email address. Impostercide solves this problem by preventing unauthenticated users from posting a comment with a registered user’s email address.


5. Sticky Comments

Sticky posts have been around in WP for quite awhile, either accomplished by plugins or themes. But what about Sticky Comments? Maybe you want to put a comment you’ve written yourself at the top of your comments list to further clarify something. Or maybe a regular commenter on your blog has provided some kind of valuable insight. This plugin lets you make their comment sticky, so it sits at the top of the comments list. It also lets you re-order comments to either newest first or oldest first.


6. Live Comment Preview

Live Comment Preview lets a visitor see how their comment will appear as they type into the comment box. It uses client-side JavaScript only (not Ajax), and even strips disallowed HTML tags from the comment in the preview. No configuration is required.


7. Minimum Comment Length

How sick are you of getting one- or two-word comments on your blog? “Thanks” or “Great post” do nothing to add to the conversation and can even make your blog look spammy. Minimum Comment Length can prevent those kinds of comments and require your commenters to leave something a bit more substantial. If they leave a comment that’s too short, WordPress will give them a friendly error message letting them know that, and asking them to leave something more useful.


8. Conditional CAPTCHA

CAPTCHA codes, while useful for spam filtering, are a big usability issue. At the same time, though, on blogs with tons of comments, it can be the most effective way to weed out spam. Conditional CAPTCHA helps solve that problem. If a comment is potentially spam (based on either Akismet or TypePad AntiSpam), the commenter will be shown a CAPTCHA code. If they don’t enter it correctly, their comment is automatically discarded or trashed, keeping your spam queue less cluttered. If they do enter it correctly, you can choose to either have their comment added to the spam queue or be approved immediately.


9. One Click Close Comments

Being able to open or close comments on a post is built into WordPress. But you have to go into each individual post to do so. What if you want to close comments on a bunch of posts (or re-open them)? One Click Close Comments allows you to open or close comments right from the post listing in the dashboard.


10. Thank Me Later

It’s a nice touch to email new commenters on your blog. Thank Me Later automates the process and automatically sends an email to thank those who comment on your blog. This can help increase engagement and make your visitors feel appreciated and valued.


11. Comment E-Mail Verification

With Comment E-Mail Verification, if a comment is held for moderation, an email is sent to the comment author with a verification link. Once the link is clicked on, the comment is automatically approved, greatly reducing spam comments. It’s also possible to set the comments to be held in moderation even after the link is clicked, if you prefer to still manually verify.


Regardless of what your comment management needs are, there’s almost certainly a plugin out there that can help you.
The WordPress plugins directory alone has over 800 plugins tagged with “comments” (just make sure you check that they’re compatible with your version of WP). And that doesn’t include all the premium plugins out there.