Friday, October 3, 2008

Telemarketers and Robo Callers

Sigh.

There's only so much attention that I can give to unwanted callers interrupting me during all hours of the day. Whether the attention I give is to annoy them in return, research them on the Internet, provide "public service messages" about the ethics of these callers and the do-not-call list, ... there's just too many annoyances in the world, and giving them any attention at all has become too much.

Although it's interesting to document and blog about "Freedom's watch against Kay Hagan", "Associated Builders Association against Kay Hagan" (both of whom called today), the democratic party doing opinion surveys yesterday, or whatever... I'm done with them. The next time I get a robo message on my voice mail, or a person calls at 9:30pm... I'm done. I'm going to delete the message. I'm going to cut the phone calls short. I'm going to stop blogging about how the do-not-call doesn't stop all the annoying phone calls, and how wrong the whole system is.

I'm done.

Wednesday, September 17, 2008

What Telemarketer Research ? For WHO ?

I got ANOTHER call today from a telemarketer claiming market research.

TELL ME YOUR CONCERNS
The call was from Mountain West Research Center (http://www.mwrcenter.com) wanting to ask me about national and local issues facing communities. As I probed for answers about who they were and who would get the results of the survey, the caller could not provided me with an answer on where my responses would end up. Why would anyone volunteer information that may go toward causes or political interests that they do not agree with. For example, lets say that I'm concerned about local businesses losing out to big corporations. Now anyone can bring up that topic to try to sway my opinion, regardless of their real intent. If I said pollution was my national concern, any corporation or political machine would know to how to gain my favor or dollars.

Are you a republican? How would you answer the survey if it was democrats calling?
Are you a democrat? How would you answer the survey if it was republicans calling?

I don't mind giving my opinions in the proper forum, but phone calling blind (which is annoying enough) and not telling me how my answers will be used. Its easy for both friends and enemies to use a person's opinion to sway or coerce them.

As an aside, I asked their company name, location, phone number... they told me they didn't have a phone number, that they were calling from the Internet (how ridiculous is that?).

YOU LIKE PET FOOD?
I had a different call recently (at 9:00 pm) researching my views on pet food products. They too weren't selling anything and felt the Do-Not-Call list didn't apply to them. I kept them on the line for a while asking about who was paying for and receiving the survey. As they said the survey results would be free, I asked them to send me the results. They then said it was not free, to which I asked who was paying for it. I had the guy talking in circles before he suggested I speak with his supervisor. The supervisor was rather unapologetic about calling anyone and about not revealing who was getting the survey. My point with these people was that it WAS a commercial phone call, and that they shouldn't be able to call me due to the Do-Not-Call List.

YOUR HOMEWORK
When they call YOU, be sure to get any identifying information so that you can look them up on the Internet, and blog about them. Get company name, address, phone number anything you can search on. Also insist that they tell you where your information is going, and who is paying for it. Good luck.

Wednesday, September 10, 2008

Enterprise Library Logging with WMI

In Enterprise Library for Microsoft (EntLib v3.1), you can use the WMI TraceListener (WMITraceListener class) to send logging events/statistics to the Windows Performance Monitor application.

Steps:

  1. Enable Instrumentation on the host: Start / All Programs / Microsoft Patterns & Practices / Enteprise Library 3.1 - May 2007 / Install Instrumentation
  2. With Enterprise Library Configuration, configure your app.config or web.config to use the WMI TraceListener.
  3. With Enterprise Library Configuration, add Instrumentation
  4. Edit the resulting config file (from previous steps) to set the instrumentation properties to "true": <instrumentationconfiguration performanceCountersEnabled="true"
    eventLoggingEnabled="true" wmiEnabled="true" />
  5. If you have subclassed LogEntry, make sure any custom typed Attributes are decorated with [IgnoreMember] (you'll have to have System.Management as a Reference)
  6. Run your program
  7. Add a new Counter in Windows Performance Monitor: Performance Object "Enterprise Library Logging Counter"; Select counters from list; Select your program's instance.
Troubleshooting:
  • If "Enterprise Library Logging Counter" isn't available in Performance Monitor, then you didn't Install Instrumentation in EntLib.
  • If you can't select an instance while adding the Counter, then your program probably isn't running, or something is preventing it from being instrumentable
  • If the application event log contains errors suggesting that you use IgnoreMember, then you are logging a subclassed EventLog that has Attributes with special types (the event log error will say which one). Or change the type of you attributes to string, int, etc... to avoid the problem.

Monday, September 8, 2008

Don't Give to Rude Telemarketers

Hey Telemarketers!
Telemarketers, when you call me midday or in the evenings, I am not happy or receptive to your message... but I am polite. I generally will try to end the call gracefully. If you are rude to me, then I start to question why I am being polite to you.

Fraternal Order of Police
Yesterday evening I received a call from the Fraternal Order of Police, asking for a donation to their noble cause. I listened and waited for a chance to reply and when I calmly said "no thank you" they immediately hung up.
Today, to my surprise, I received another call from the Fraternal Order of Police (in the afternoon). I interrupted the telemarketer a few times to make sure that I was talking with the same organization, as I made notes. Then I interrupted again and said that they had called yesterday and were rather rude to me. I conveyed that they hung up on me after I had politely refused. As this telemarketer tried to break in, I said that I was NOT happy with them, and I hung up. I'm surprised that I was able to hang up on them before they hung up on me.

March of Dimes
I like the intent of this organization, but their fund raising efforts are ruthless. The March of Dimes has called my house asking for my wife... I'll ask who they are. After they respond I tell them that my wife isn't around, but I can help them. At that point they tend to hang up without further response. Later (sometimes hours, sometimes days) they call back, identified by caller ID, and hang up quickly when they find out that my wife isn't available. To me, that's rude and devious. It is also impossible to get off their mailing list.

Not Over the Phone
Additionally, stop insisting that I give right now! I tell anyone who calls that I do not make donation decisions over the phone. Send me information in the mail, and I'll make my decisions casually when I sit down to pay bills. I've had telemarketers hang up when I tell them that I do not commit to donations over the phone, but that I would like them to send information to me.

Who are the Telemarketers Helping? Themselves?
Some of the telemarketers that call me are not doing justice to the charities that they represent. Either through poor phone etiquette, or not providing a high enough percentage back to the charity. The telemarketers that are now blocked by do-not-call lists seem to have moved to calling for charities. They see the potential for raising money for a charity, the potential to get around the do-not-call list, and a way to make profits again. I've asked some of the telemarketers that call me about what percentage goes to the charity... some don't know, some are low percentages, and others are more reasonable. I don't know how March of Dimes works, but do your own googling on the telemarketers calling for Fraternal Order of Police... I've seen horrible reports stating the charity gets as low as 12%. All of these comments are about the telemarketers representing the charities. I'm sure the charities are worth while, but ask some questions and do some quick research (Google) before you give to any charity through a second party service (ex: what percent goes to the charity?)... that's why I don't make decisions over the phone!

Give, but Not to Telemarketers
I do suggest that everyone find a charity they feel is deserving, and find a comfortable way to donate to them. Each year I tithe to our church and give to some charities. I'm also on the board of NathanCan Foundation (NathanCan.org) working to help chronically sick kids. If I weren't giving or directly participating in a charity, I'd feel like this entire discussion was merely an excuse not to give anyone anything... but there ARE good and deserving causes out there, and GOOD ways to donate.

Sunday, August 24, 2008

Porting YetAnotherForum.NET to SharePoint

Idea

YetAnotherForum.NET (YAF.NET) on SharePoint, sounds like a great idea!
(The main focus of this article is partitioning data between YAF.NET forums based on SharePoint site.)

It sounds like YAF.NET might already handle authentication of users arriving through Active Directory (ie: auto register), but I don't want just a YAF.NET install next to my SharePoint system. My needs are to have YAF.NET be a feature that can be deployed across hundreds (thousands?) of SharePoint sites without intermingling the content (Topics/Posts) between sites.

It seemed like it might be a weekend effort, but with family activities, other personal projects and the size of the effort, I've decided that I just won't have the time to port YetAnotherForum.NET to SharePoint 2007 (WSSv3). I will share my findings though.

Approach #1

Inversion of Control, decoupling the data access mechanism from the application so a new data store (ie: SharePoint) could be injected. The approach would be to take all the DB access methods in YAF.NET, and make them use an interface, and put the existing DB access code into a class to satisfy that data interface. The phase 1 result would be a code refactored YAF.NET that works exactly the same as it did.

The second phase of the port would be to create another implementation of the data access interface that utilizes SharePoint lists instead of the SQL database. Plug in that new interface, and the YAF.NET should work fine without knowing that its running on SharePoint. It should be possible to satisfy all the database needs of YAF.NET with SharePoint lists and CAML for access. The only need that might not work is YAF.NET's free-text search capabilities.

Data will need to be compartmentalized into the SharePoint sites that YAF.NET was enabled for. This will help application scaling and content ownership. There are some practical data limits to SharePoint's core list capabilities, but utilizing separate YAF.NET lists per SharePoint site avoid those issues. The YAF.NET SharePoint data access would have to utilize the lists for the current site to make this work, would be pretty easy once the rest was in place.

Example YAF.NET loading from SharePoint site context:
http://hostname/sites/TestSite/_layouts/YAF/default.aspx

The SharePoint site at "/sites/TestSite" would have the normal YAF.NET data sources:

[...]

The entire YAF.NET for SharePoint system would be packaged as a SharePoint site collection or web scoped feature in a SharePoint solution file. That means that once the solution has been added to your SharePoint system, site administrators could go to Site Features (or Site Collection Features) and enable that feature. The initialization of the feature would run, creating the proper lists and default data.

This approach sounds like the best SharePoint solution, but without in-depth knowledge of YAF.NET, it feels like 3-4 weeks worth of development and testing.

Approach #2

A compromise solution to porting YAF.NET to SharePoint would be to partition the data for SharePoint sites within the current YAF.NET SQL database. By adding a new "Context" column to all existing tables, then slightly modifying all the SQL in YAF.NET, the system could function within the context of the SharePoint site it was accessed from. This approach simulates different data stores for each SharePoint site, but does not change the native source YAF.NET already uses, so the approach is less complex.

This approach sounds like a reasonable SharePoint solution, and avoids some of the questions about SharePoint list data scalability issues by continuing to use native SQL Server. Because this approach is generally many simple modifications, it feels more like 1-3 weeks worth of development and testing.

I started some work down this approach to flush out the main issues. Some of these steps are required for either approach...

Live in SharePoint Layouts
YAF.NET would need to function in the common SharePoint "layouts" directory. This is where common SharePoint functions/systems need to live, and they are given the opportunity to be "aware" of the site context that they are invoked from. This part was quick and easy.

Misc:
In \web.config, I enabled session state, and switched trust level to full. Neither is a great idea for SharePoint, but worked for my development purposes...
pages enablesessionstate="true" ...
trust level="Full" originUrl=""

For this url to work:
http://hostname/sites/TestSite/_layouts/YAF/default.aspx

...YAF.NET needs to be copied to:
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\YAF
(I put the YAF.NET binaries into the webroot/bin for testing)

Config.cs needs to know about site context:


///
/// Determine the site context from the http request.
/// /
/// /sites/TestSite
///
static public string UrlContextPath
{
get
{
string scriptUrl = HttpContext.Current.Request.ServerVariables["SCRIPT_NAME"];
string rawUrl = HttpContext.Current.Request.RawUrl;
int pos = rawUrl.ToUpper().IndexOf(scriptUrl.ToUpper());
if (pos <>
{
return "/";
}
return "/" + rawUrl.Substring(0, pos);
}
}


UrlBuilder.cs needs to build urls with site context:

public string BuildUrl(string url)
{
return string.Format("{0}{1}?{2}",
Config.UrlContextPath.Substring(1),
HttpContext.Current.Request.ServerVariables["SCRIPT_NAME"],
url
);

//return string.Format("{0}?{1}",HttpContext.Current.Request.ServerVariables["SCRIPT_NAME"],url);
}


Partition Data by Site Context

This is the BIGGEST porting task, requiring changes to all 31 database tables, 1 view, 162 stored procedures, and all corresponding code invocations of SQL and stored procedures.

Add the Context field to all database tables. For example:

IF NOT EXISTS (SELECT 1
FROM sysobjects
WHERE id = Object_id(N'yaf_AccessMask')
AND Objectproperty(id,N'IsUserTable') = 1)
CREATE TABLE dbo.yaf_AccessMask (
[AccessMaskID] INT IDENTITY NOT NULL,
[Context] NVARCHAR(255) NOT NULL,
[BoardID] INT NOT NULL,
[Name] NVARCHAR(50) NOT NULL,
[Flags] INT NOT NULL CONSTRAINT DF_yaf_AccessMask_Flags DEFAULT (0))
GO

IF NOT EXISTS (SELECT 1
FROM syscolumns
WHERE id = Object_id('yaf_AccessMask')
AND name = 'Context')
BEGIN
ALTER TABLE dbo.yaf_AccessMask
ADD Context NVARCHAR(255) NOT NULL DEFAULT '/'
END
GO



Modify all SQL and Stored Procedures to use the new Context field. For example:

CREATE PROCEDURE [dbo].[yaf_accessmask_delete](
@AccessMaskID INT,
@Context NVARCHAR(255) = '/')
AS
BEGIN
DECLARE @flag INT
SET @flag = 1
IF EXISTS (SELECT 1
FROM yaf_ForumAccess
WHERE AccessMaskID = @AccessMaskID)
OR EXISTS (SELECT 1
FROM yaf_UserForum
WHERE AccessMaskID = @AccessMaskID)
SET @flag = 0
ELSE
DELETE FROM yaf_AccessMask
WHERE AccessMaskID = @AccessMaskID
AND Context = @Context
SELECT @flag
END
GO



Modify all invocations to SQL and Stored Procedures to use the new Context field. For example:


static public bool accessmask_delete( object accessMaskID )
{
using ( SqlCommand cmd = new SqlCommand( "yaf_accessmask_delete" ) )
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue( "@AccessMaskID", accessMaskID );
cmd.Parameters.AddWithValue("@Context", Config.UrlContextPath);
return (int)ExecuteScalar(cmd) != 0;
}
}



With a few of these changes in effect, YAF.NET continued to function properly, which helped prove the viability of the approach, but also proved that it would take a non-trivial amount of time to complete. I did not continue the effort due to my lack of availability.

Additional Issues:

Web Part - These approaches are targeted at making YAF.NET work as a companion to SharePoint, not fully integrate it. An expectation of any SharePoint admin would be how to drop a dynamic view of the forum onto a SharePoint Web Part Page. Luckily YAF.NET is constructed with web user controls, which translate pretty easily into web parts. Look at SmartPart WebPart (or other sample code) for how to load a normal web user control in a Web Part container.

Isolation - Some elements of YAF.NET need to be shared rather than isolated between SharePoint web sites. Data such as Users, Permissions and Access Masks are some examples of data that might need to be shared across all SharePoint sites (especially users!)

Overlapping domains - Some elements of YAF.NET need to pull data from SharePoint rather than handled by YAF.NET. For instance, Users and Permissions should come from SharePoint, otherwise SharePoint admins will need to setup users/permissions TWICE (once for SharePoint sites, and another time for YAF.NET).

Default data - Some data is provisioned during the one-time YAF.NET install. Some amount of data provisioning may need to occur once per SharePoint site. During setup/initialization. For instance, the AccessMask is setup with some default permissions. If data in YAFF.NET is merely partitioned between SharePoint sites, that one-time data initialization will only benefit one SharePoint site.

Monday, August 18, 2008

Microsoft Update - the following updates were not installed

I kept receiving "the following updates were not installed" when trying to use Microsoft Update on one of my plain Windows XP VMware machines. I have HEAVILY relied on Microsoft Update for a long time for many machines, and this is the first time updates just wouldn't install. In my scenario, I would choose "Custom" and anything I chose would go through the motions, but ultimately fail to install.

I followed the simple instructions here which solved my problem: Updates are not installed successfully from Windows Update, from Microsoft Update, or by using Automatic Updates after you perform a new Windows XP installation or you repair a Windows XP installation

Method 1: Register the Wups2.dll file in Windows

To register the Wups2.dll file in Windows, follow these steps:
1.Stop the Automatic Updates service. To do this, follow these steps:
a. Click Start, click Run, type cmd, and then click OK.
b. At the command prompt, type the following command, and then press ENTER:
net stop wuauserv
2.Register the Wups2.dll file. To do this, follow these steps:
a. At the command prompt, type the following command, and then press ENTER:
regsvr32 %windir%\system32\wups2.dll
Note For a computer that is running Windows XP Professional x64 Edition, type the following command, and then press ENTER:
regsvr32 %windir%\syswow64\wups2.dll
b. Click OK on each verification message that you receive.
3.Start the Automatic Updates service. To do this, type the following command at the command prompt, and then press ENTER:
net start wuauserv
4.Exit the command prompt. To do this type exit, and then press ENTER.

Sunday, August 10, 2008

Service manuals are very useful !

My printer broke and I was confident that I'd be able to fix the simple problem once I downloaded the service manual. I have to say it was very cool to be able to get a hold of that manual ! It has instructions on how to diagnose, disassemble and fix. The printer went through the motions, just didn't lay any ink... must be clogged, should be easy to fix.

The printer actually stopped printing about 2 years ago, and I had since abandoned the overly expensive and questionable ethics of that printer's manufacturer. Since I HAD purchased extra printer ink cartridges with that company's settlement to the class action lawsuit (pertaining to questionable ink practices)... I wanted to use up that ink, and spare some ink on my new Canon PIXMA MP780.

I have to applaud any company (even the maker of this printer and its expensive ink) that has a service manual that I can find online (I got it at this site). Kudos ! I also found community help for exactly the problem I had. Regardless of technical expertise or skills, I think everyone should have the chance to get the full manuals and self-help for all the products they own.

My project started following A FIX for Clogged Printheads which was VERY nice. The disassembly, construction of make-shift tools, and use of anti-freeze... all went great removing tons of gunk from my old printhead. Followed all instructions, didn't help in the end. I was still happy and empowered by the community help.

Then I found and dove into the service manual. I wouldn't gotten nearly as far carefully and gracefully locating and removing the proper screws without it. I usually break things as I'm being careful, which I didn't do as I followed the manual.

As the manual pointed me at screws that could only be reached with long narrow specialized screwdrivers I started to get frustrated, but still thinking there was SOME hope that I'd be able to reseat them all and get this thing back together.

Having to ultimately go for removing the printhead itself (ugh), I abandoned the manual, and honestly all hope of ever getting this darn thing back together. Just think though, without the manual I wouldn't have ended up with this, or potentially in the mood to buy a new printer. Too bad I already decided I didn't like this one's manufacturer, I might have bought another of their printers merely because I was empowered with the service manual to destroy my old one!


Care with the service manual lead to not caring and destruction.

Well, at least the 3 day project is now over, and I've got some additional neat parts for my Junk box.