2009
11.02

I got a bit tired of not having user ignore possibility in smf 1.1.x series, and I found some scripts mostly for phpbb though there were some oldish hacks for smf too. I took one of those, and updated it to somehow match the 1.1.x series and newer GreaseMonkey (tested on 0.8.2…). Very simple to use, so no instructions needed :) Just change the line 27 to match you installation, this one is localized to match finnish language set.

Just save it to desktop like smf_userhide.user.js and open it with firefox with GreaseMonkey installed.

UPDATE: Just copy and paste the following snippet. It works okay, It’s just the WP-Syntax that seems to cut the lines. The whole content is copied anyway.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// ==UserScript==
// @name          SMF User Hide
// @include       */index.php?topic=*
// @description   hides / unhides a user's posts - based on User Classes by Unarmed
// @exclude
// ==/UserScript==
// Taken from
// http://forums.mozillazine.org/viewtopic.php?f=19&t=236218&start=45, Authored
// originally by Unarmed, Updated 2.11.2009 to match new GreaseMonkey and SMF
// 1.1.4 by Juha Heimonen
 
(function() {
   // Get stored hidden users from cookie
   var users = [];
   var cookieName = "smfUserHide";
   for (var i = 0; i < document.cookie.split('; ').length; i++) {
      var oneCookie = document.cookie.split('; ')[i].split('=');
      if (oneCookie[0] == cookieName) {
         users = oneCookie[1].split(', ');
         break;
      }
   }
 
   // Find all the usernames in the page, to change to different locale, edit
   // this line to reflect that change
   var results = document.evaluate("//td/b/a[starts-with(@title, 'Tarkastele profiilia')]", document, null,
      XPathResult.ANY_TYPE, null);
 
 
 
   var resultNodes = [];
   var aResult;
   while (aResult = results.iterateNext()) {
    resultNodes.push(aResult);
 
    }
   // Loop through every user post on the page
   for (var i in resultNodes) {
 
      var containingRow = resultNodes[i].parentNode.parentNode.parentNode;
      // Format whitespace
      var user = encodeURI(resultNodes[i].innerHTML);
 
      // Flag whether the user is in our hide list
      var notFound = true;
      for (var j = 0; j < users.length; j++) {
         if (users[j] == user) {
            notFound = false;
         }
      }
 
      // Add relevant event handlers to a "[X]" node in front of user's name
      // On click, add or remove this user from the stored user list in the cookie
      var toggler = document.createElement('span');
      toggler.title = "click to add or remove this user from your hide list";
      toggler.appendChild(document.createTextNode('[X] '));
      toggler.namenode = resultNodes[i];
 
      toggler.addEventListener('mouseover',function(event) { event.target.style.cursor = 'pointer'; },true);
      toggler.addEventListener('mouseout',function(event) { event.target.style.cursor = 'pointer'; },true);
 
      toggler.style.fontSize = "7pt";
      resultNodes[i].parentNode.insertBefore(toggler, resultNodes[i]);
 
      toggler.addEventListener('click',function(event) { for(j = 0; j < document.cookie.split('; ').length; j++ ) {
 
            var oneCookie = document.cookie.split('; ')[j].split('=');
            if (oneCookie[0] == cookieName) {
               users = oneCookie[1].split(', ');
               break;
            }
         }
 
         var tgt = event.target;
 
 
         /
 
         var user = encodeURI(resultNodes[i].innerHTML);
 
 
         notFound = true;
         for (var j = 0; j < users.length; j++) {
            if (users[j] == user) {
               users.splice(j, 1);
               notFound = false;
            }
         }
         if (notFound)
            users.push(user);
         if (users.length > 0) {
            var date = new Date();
            var days = 365;
            date.setTime(date.getTime() + (days*24*60*60*1000));
            var expires = '; expires=' + date.toGMTString();
            var value = users.join(', ');
            document.cookie = cookieName + '=' + value + expires + '; path=/';
         } else {
            document.cookie = cookieName + '=;expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/';
         }
         alert(decodeURI(user) + ' has been ' + (notFound ? 'added to' : 'removed from')
            + ' your hide list\n'
            + 'You must refresh the page to view the changes.'); 
 
 
 
            },true);
 
 
 
      // If this user isn't in our hide list, skip to the next user
      if (notFound)
         continue;
 
      // Find the first element node in the containing row
      var elem = containingRow.firstChild;
      while (elem.nodeType != 1)
         elem = elem.nextSibling;
 
      // Create a span to control toggling
      var span = document.createElement('span');
      span.appendChild(document.createTextNode('Toggle Display'));
      span.setAttribute('class', 'smalltext');
      span.style.textDecoration = 'underline';
      span.style.fontWeight = 'bold';
      span.setAttribute('displaystate', 'none');
      //span.onmouseover = function(event) { event.target.style.cursor = 'pointer'; };
      span.addEventListener('mouseover',function(event) { event.target.style.cursor = 'pointer'; },true);
      //span.onmouseout = function(event) { event.target.style.cursor = 'default'; };
      span.addEventListener('mouseout',function(event) { event.target.style.cursor = 'default'; },true);
      //span.onclick = function(event) {
      toggler.addEventListener('click',function(event) {  
      var displayState = event.target.getAttribute('displaystate');
         if (displayState == 'none')
            displayState = '';
         else
            displayState = 'none';
         event.target.setAttribute('displaystate', displayState);
         // Target user information
         elem = event.target.nextSibling;
         while (elem.nodeType != 1)
            elem = elem.nextSibling;
         if (elem.getAttribute && (elem.getAttribute('class') == 'smalltext'))
            elem.style.display = displayState;
         // Target post body
         elem = elem.parentNode.nextSibling;
         while (elem.nodeType != 1)
            elem = elem.nextSibling;
         if (elem.tagName.toUpperCase() == 'TD')
            elem.style.display = displayState;
         // Target post footer
         elem = elem.parentNode.nextSibling;
         while (elem.nodeType != 1)
            elem = elem.nextSibling;
         if (elem.tagName.toUpperCase() == 'TR')
            elem.style.display = displayState;
      },true);
 
 
      // Insert the span after the username and before the <br>
      elem.insertBefore(span, elem.firstChild.nextSibling.nextSibling);
      // Insert a <br> after the username and before the span
      elem.insertBefore(document.createElement('br'), elem.firstChild.nextSibling.nextSibling);
 
      // Crawl down and remove the postdetails span
      elem = elem.firstChild;
      while (elem) {
         if (elem.getAttribute && elem.tagName.toUpperCase() == 'DIV'
            && elem.getAttribute('class') == 'smalltext')
            elem.style.display = 'none';
         elem = elem.nextSibling;
      }
 
      // Reset the elem pointer to the first table cell in the row
      elem = containingRow.firstChild;
      while (elem.nodeType != 1)
         elem = elem.nextSibling;
 
      // Move to the next table cell in the row
      elem = elem.nextSibling;
      while (elem.nodeType != 1)
         elem = elem.nextSibling;
 
      // Move inside that table cell and remove the postbody and postsig spans
      if (elem.tagName.toUpperCase() == 'TD')
         elem.style.display = 'none';
      elem = elem.parentNode.nextSibling;
      while (elem.nodeType != 1)
         elem = elem.nextSibling;
      if (elem.tagName.toUpperCase() == 'TR')
         elem.style.display = 'none';
   }
})();
2009
10.27

Software development is indeed more human that we maybe are likely to admit. Because I’m such a great fan of different analogies, I’d use this space to tell a little story that happened a while ago.

I have a friend who tries to get pregnant. There’s only one little thing that is messing up the plan, and that is her fear of giving birth. As we did a bit of research about the fear of giving birth, we noticed that it seems to be increasingly common thing among first time birth givers, and when she contacted a nurse at her school, she got very well understood. There’s also the thing that she’s suffering from a condition called fibromyalgia, which doesn’t make things any easier for her.

With the nurse, they agreed that she’ll visit a local birth clinic and talks with a doctor and a nurse at the birth ward, so that they could answer some of her questions and talk about different birth giving options the ease her fear. This sounded a really good idea, and the school nurse explained that these things are nowadays handled quite well: child birth is quite a valuable thing in our society, and is quite normal and common to get things straight beforehand. So she got the appointment to the hospital, and everything seemed to go fine.

Came the day of the meeting at the hospital. I knew about the time when she was going to get into the meeting, and I was anticipating her to call me in about two hours from the start time. Instead I got a call from about 20 minutes from the meeting start: she was sobbing and crying.

The nurse at the birth ward had said to her that “why are you here if you aren’t even pregnant yet? I can’t help you”, and had treated her in a way that if you are afraid of giving birth, maybe you shouldn’t. She also didn’t understand why my friend had even thought about giving birth that much, for the nurse it seemed to be the most obvious thing in the world, and she couldn’t cope with different views. So as a result my friend practically fled from the appointment crying.

This was very far from what the school nurse had promised.

2009
10.18

Okay, I have read a good book. And the thing is, that after you have read a good book, you tend to get lots of influences. This can be quite a bad thing, given that the book isn’t absolutely good, and you still think so. On cases where you also can disagree with the author, but agree on some parts, you are generally on safer ground.

Anyway, there’s not much arguing with the fact that Alan Cooper’s The Inmates are running the asylum doesn’t hit some serious sore spots in our software designing. Even though Cooper is a bit too much in love with the concept of Dancing bearware (software that does the job it says it does, but does it fucking poorly is a constant amazement for us, in a false positive way), he’s never the less, bloody right.

Personally I have often felt very uncomfortable with software products. I don’t know if that’s because I have a strong past as a Test Engineer, could be. Anyway, I can easily spot the divider that pushed me over the line what comes to actual product designing. That was a time when we had to design and implement an automated testing framework for a product we actually were testing ourselves at the time. The problem was that the customer owned the actual testing concept, and we had hard time to make them believe, that we would not actually need another scripting language or whatnot. We did good enough with the implementations we had. The only problem was that we couldn’t reuse them and store them properly. And we designed a product what would fit to our needs, where we could arrange the test scripts to modular test sets and run them against any environment. That was a perfect fit to our need.

Needless to say, we failed. Mostly because the customer had consultants, who weren’t actually interested about the Test Engineer involved in our project. Yes, they were interested about ‘user’. They had been in contact with test automation framework users in another projects, and naturally wanted to ship those ideas to this new project. Well, needless to say, that the implementation we were forced to use, is not in use anymore. The whole project was nicely wasted six months of everyone’s time.

Same thing, with a different twist is happening in my current project assignment. Although, this time I’m not in the ‘user’ group in that sense. The problem is, that we really don’t know who is, and yet we are already going on in release 4.0, 5.0 or whatnot… There are other problems also, which I unfortunately can’t address here. Alan Cooper gave more insight to the problems that exist. And a book that gives true value to your profession, must be hellishly good one. Things presented in Cooper’s book are not implementable in all places. The project I’m working in is one such place, but if we don’t even try our best on correcting the mistakes we have made before, and what is worse, if we even try not to recognize the sore spots we are exhausting both our users and ourselves.

2009
05.06

A quick reminder for my self. Never, ever anymore use


include name="**/*.jar"

with classpath in application root in ant when working with tomcat and deploying web applications. Get’s things complicated, since it tends to do double entries. Took about 5 hours of work. Geesh,

2009
04.20

As things usual, everything does not go as espected. When adding projects to Jira, you usually must set default permission sets and assing users to grops and what not. One especially irritating command (from Python point of view) in Jira’s SOAP api is

addActorsToProjectRole

which is quite useful, a must, when setting group permissions to specific project. When using SOAPpy, the correct way to call the method is:

soap.addActorsToProjectRole(v4=proj,  v1=helper.token,v3=projectrole, actorType="atlassian-group-role-actor",v2=[group])

This add’s group actor, refer to Atlassian documentation to add user-actors to project roles. Remember that you can get project and project roles through soap by using jpypye and casting Python longs to Java.

Don’t ask me why, but this seems to render acceptable xml that Jira’s serverside implementation can read. A bit odd, but Atlassian supports only Java on SOAP side, so probably you’re more safe with Axis.

Another unfortunate thing concerns project creation. (This is to remind myself): WHEN users or groups have been added to project roles, like for giving permissions to delete work logs etc (and this usually concerns the defaul perimission set), we must remove the entries for users by walking through the permiossion set like this:

1
2
3
4
5
6
7
8
9
10
for y in permissionset[3]:
 
                #print y
                if len(y["remoteEntities"]) > 1: 
                    try:
                       y._placeItem("remoteEntities",None,1)
 
                    except Exception, e:
                        print e
                        pass

permissionset variable holds the Permission-set object, and slot [3] holds the actual permissions. If additional users or groups are added to the permission set, then permission (remoteEntities) will hold those users in a list, which must be “deleted” by assigning None. Otherwise the soap will complain about ‘email’, or ‘name’ fields. So that does it in Python.

2009
04.19

Authority is something that generally bugs me. What bugs me more, is situations where I’d need some but haven’t got. What irritates me most is the people that have false sense of authority. Unfortunately these people tend to get into semi-high places in workplaces, I think that’s called middle-management. Or marketting, but as we know, sales people are satan’s little helpers. I seriously hope that these are problems only found in big or semi-big companies. Maybe it is so that if the company thinks that it is big, then se semi-big people also think that they’re huge. And that’s the point where I generally generate some attitude problems. Rightfully? Well, sometimes I tend to think so.

Companies, big or small, probably need management culture. At least some kind of line from which the managers and people in charge generally don’t deviate that much. I agree that there can also be positive deviation, but mostly things that I see and have to deal with are pure of arrogance. Our company is actually doing something to minimize these deviations, and it only struck me today when I realized that these ‘values’ are actually focused to management. Probably for all others they’re quite obvious (listening workmates, team spririt, giving value to workmate’s effort…). It’s interesting to see how this will come out, though I’m troubled by the thought that those who should give a shit, will not.

So, the problem, as it seems is the cross-project communication. Expecially in those projects that would need consultancy in areas they’re trying to extend their customer relations. There’s a spot where an “authoritarian” sales man or project lead can do some serious harm by not listening to those that have the bigger experience in area in question. I’m curious to see how these company values would affect these situations, since as it seems, I lack the right badges in my collar to get my advice noted. Sucks, big time.

2009
04.16

Jira is one of my favourite applications currently. Together with GreenHopper it gives a fine way to manage agile projects. It’s really okay, give it a try. But most importantly, Jira has SOAP API, which grants possibility for external integrations. Only sad part is that Atlassian only supports Java clients, and my tool for trade is Python.

One of the most irritating problems when using SOAP through python is the conversion of Python’s long to BigInt in java. As Jira uses long-type in id-fields this causes Bad type exceptions. Fortunately there’s one thing that comes very handy: it’s called JPype: http://jpype.sourceforge.net/, though probably any Python to Java bridge works. Here’s a little bit that does the trick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SOAPpy
 ...
 
 from jpype import *
 startJVM("C:/Program Files/Java/jdk1.6.0_07/jre/bin/client/jvm.dll", "-ea")
 permissions = self.soap.getPermissionSchemes(self.token)
 
 #Now let's change the id of permission set to Java long from python long:
 permissions[0]._placeItem("id",java.lang.Long(permissions[0]["id"]),1)
 
 #iterate through permission set's child elements and do type conversion:
 for i in permissions[0]["permissionMappings"]:
            i["permission"]._placeItem("permission",java.lang.Long(i["permission"]["permission"]),1)
 
 #now just create the project:
 self.soap.createProject(self.token,"PROJ","Projname","info","http://whatever.fi","lead",permissions[0],None,None)

Basically we just cast the Python long’s used in id-fields to Java long’s and Jira’s is pleased. For serious clients it’s adviced to extend the SOAPpy’s struct class to mimick the actual java objects like remote-permissionsets and so on so we act with custom made objects, but you get the idea from here if you need.

This bit is also posted as a comment to Jira’s SOAP client Confluence page: http://confluence.atlassian.com/display/JIRA/Creating+a+SOAP+Client

2009
04.15

About being good

This is a fresh start. Too tired to start a whole new blog, and since this echoes of the past should not be forgotten, let them just be. And anyway, I still hope that on some day I still have time to do some actual tinkering besides just working. And there it was, the unfortunate word: work. The word that has a pretty much stronger echo in my ears than in the past. Not because my work itself would be glamorous, nooopey. I just have a loan to pay that’s all. So from here now on, I’ll mostly be talking about things in general, and that’s just fine. Today, it’s about being good.

And to be a bit more accurate: to be good at what you do, which kinda intersects with this working thingie, because well, I _want_ to be good at what I do. Also at work. Emphasis on the word: want. It’s not my right to say if I am, but I reserve the right to say _who_ do _not_want_. You see the little difference there? Thought so. In the organization I work for, it’s not so obvious that people want to be good at what they do. It’s probably more a question of “managing” at what they do, and sometimes not even that. And well, it’s sometimes a bit problematic.

I’ve been told that it is a part of the culture at our workplace. That disturbs me. You see, I’m pro multi-culturalism. And this is something that crosses my idealism. That was a joke, and actually I’m not disturbed. I’m just blindly raged. We cannot all be good (except that we can). I’m lousy at quitting smoking, though I sometimes try and manage for a while. This is one type personnel at workplaces actually. One tries and tries, until something new comes up that cannot be understood straight away, so one stops trying.

Again, if you ask my girlfriend if I’m good at cleaning up the house, she’ll say that I’m good at cleaning bathrooms but otherwise I suck. And that’s true, and what’s worse, I don’t have any interest about trying to learn proper ways of cleaning our wooden floor and all nyances alike. But I know I should, because at the moment I’m relying to my girlfriend on doing those things, and that probably irritates her a bit. This is also one stereotype often found from, not only mine, but other workplaces as well: the persons who have been in the house for 5-10 years and if they are forced to do something that does not fit to their expertice, they’re quiet. So quiet that in the last few weeks of the project, other people will have to cover gaps that they left.

There’s also one other version of this hermit: the one that comes to your cubicle every day, asking “how do I do this?”. This is actually quite fine, because I believe that he/she _wants_ to be good and is ready to make some effort. But when the question is phrased like this: “Tell me again, how did we do this last time?”, the condition turns red because the ship is sinking.

It’s about wanting to be good. This is about wanting to be good. About the things I want to learn and understand. This is the purpose of Tinkeridoo.

2008
03.03

This isn’t so much related to gaming, sorry.

Okay, Finnish public television company (don’t know the official name, but we call it YLE), offers some of their series and programs in internet for all people to watch, should you have missed in on broadcasting time. A nice thought, really. The site is called “Yle Areena”, and is quite nice to use. There’s just one thing.

Well, I tend to use Linux. And you guessed right, the streams are some kind of WMV things, capsuled to that you cannot watch them without making some extensive codec magic. That’s just so freaking wrong, if you ask me. Luckily we have the good software called Mimms, which comes to the rescue (this due to fact that watching WMV-streams through some mplayer plugin is generally… well, not so nice, so it is easier to just rip the stream to a file).

I made a python class that handles the ripping for me, and a cgi-interface to that (not so cleanly and nicely done, but does the trick). So here it is. This due to the fact that I have a mythtv setup at home, and a webserver running, so I can just pull a laptop and fill the url from YLE and push a button to have the stream ripped to the mythbox. Nice. The url to use is the “Ohjelman suora osoite” -one.

And remember to confugre cgi things right from htaccess or from apache if you use it like that.

Areena Ripper

NOTE: There’s some shitty streams that this class isn’t capable of ripping yet. Sorry about that.

2008
02.03

For a long time I have had the urge to take a closer look at the database systems developed to Neverwinter nights. I have had the impression that the build-in database system isn’t actually so good for permanent worlds, and that’s what were aiming at. I thought there would be some hack that would have somehow added ‘native’ support for most common database types, but I was wrong. Fortunately I found a solution from Nwn2x.

I have to say that I don’t like it. Meaning, that this is something that should have been added to nwn natively, and from the start. But it seems that the Nwn2x solution does the magic of adding database functionality as smoothly as it can be done: by adding an extra layer to the server process. This is something I don’t like, but the need for databases overcomes this uncomfortableness.

And this seems to work. I like the idea of having some kind of IPC between neverwinter nights and some other application. I can now use mysql-database for these needs. More common use would be saving of some permanent variables of course, which would allow better plot developement, but this IPC thing is so much stuck to my mind that I have to do some tinkering related to that. Besides that speaking signpost I made for demo purposes today, that’s not exactly sensible. But feeding the input for the sign post from WWW-form is cool :)

Anyway, need for database connectivity in Neverwinter nights? Check the NWN2X, the linux version works fine, even if it is a bit outdated.