Join us on IRC | Message Archives | edev FAQ

edev is the 'Everything Developer' usergroup, for people interested in contributing to the development of this site, or just learning about how it works. Please /msg Oolong if you want to be added.

As in previous months, I have concentrated on coding visible features to impress the masses, while the likes of Oolong and OldMiner grind away behind the scenes doing essential but less visible work to stop the site melting down or freezing up, depending on the weather and the current sign of the first derivative of the gravitational potential field with respect to distance from the centre of the Earth. And just to make sure everyone knows who to thank for the new bells, whistles, and errors here is a summary of what I have been doing. Much of it followed on 'logically' from the things I was doing before to improve writeup footers:

  • If you send a message to the author of a writeup from the blab box under the message, the report of the sent message is no longer jammed along with the blab box and their fourteen children in a microscopic flea-infested box where they have to stand on each others' heads in order to breathe, but has a well-ventilated space of its own under the writeup footer (or header, if you're really old-fashioned. Are you still wearing those socks?).
  • But why should a message have to wait to be sent to be able to breathe? Writeup message boxes now expand to accommodate the text within them, as do many others. There was already code in place to make multi-line textarea elements expand, but it couldn't be used for text input boxes, since these can only be used for a single line of text. So the input boxes have to be replaced by an expanding text area of the same starting size as the original text input, and then have to be watched to see if the user hits the return/enter key, whereupon the form containing the box is submitted, as is normal with an input box (but not with a text area).
  • Making the message box in the Chatterbox nodelet expand was less easy, because under some circumstances the entire chatterbox is replaced by the magic of AJAX and the expanding replacement message box would be replaced as well. AJAX is a technology for replacing bits and pieces of web pages instead of reloading the whole page, and is generally thought to be a Good Thing; you can enable it by selecting 'Everything2 AJAX' at the Javascript Repository. So the chatterbox AJAX code needed some rewiring: (1) to use an onsubmit function on the chatterbox form to trigger it, rather than event handlers on individual form controls and (2) to update the chatbox with only one AJAX call back to the server, rather than one call to sendMessage and one to updateNodelet. This in its turn required some reorganisation of the updateNodelet htmlcode to execute opcodes and to execute nodelet code in the same environment as when nodelets are inserted into a complete page, adjusting the values of the $NODE variable and the query parameters appropriately. A desirable side-effect of all of that was that the AJAXified Chatterbox is twice as fast and reports sent messages. Two desirable side-effects.
  • Meanwhile, OldMiner redefined the meaning of 'success' in the Everything2 AJAX update function, which did away with the distressing tendency of the Chatterbox nodelet to occasionally vanish completely. Building on that, a callback function in an E2AJAX call is now always called after any attempted update, not only after 'successful' ones, making it possible for the callback function to decide what do about failure. Such as provide a bland error message. Code is in place to provide informative messages and not just "(unknown error)," but I haven't seen it say anything useful yet.
  • Those of you who created a website back in the good old days will remember how you had to everything twice: once for proper browsers, and once for the one everyone was using. Those days are past. Nearly. But to make the AJAX chatbox work in Internet Explorer 8, a 'get' to had to be changed to a 'post' so that IE will not use a cached response. Telling it to tell the server not to use any response cached after 1970 is simply not enough. What a rebel that William is.
  • Since I was messing around with the Chatterbox, I had a look at chatterlight. This has completely differently structured html code from the rest of the site, for which reason I excoriate and anathemetise it, and spit on the graves of its Reverend Progenitors. I also made a candidate replacement for it, currently to be seen at Chatterlighter. Since this provides absolutely nothing of any use to users of this site that is not also provided by chatterlight, its candidacy is currently in abeyance. One day it may be customisable in Exciting Ways in the context of pioneering new options for customisation of the site as a whole, whereupon its bid will be renewed. Please do not hold your breath, as resuscitation is cumbersome and not always 100% successful.
  • To make Chatterlighter as a bog-standard Everything2 superdoc it was necessary to make it possible for pages to define their own nodelet set, overriding or adapting the user's normal nodelet options. Having done that it was possible to make sure that when a nodelet is viewed on its own page it will not also be shown in the page sidebar. Nodelets can contain forms that do things which are better not done twice, and they have id attributes in their html code which should only be used once in a given page, so that was a Good Thing.
  • When Chatterlight(er) is inactive for a long time it no longer puts up an alert and steals the focus from whatever interesting things were keeping you from following the current discussion of the relevance of boobies to post-modern deconstruction of late-feminist neo-Quaker Quantum Mechanics. Instead it simply notes that it has got bored, provides a button to wake it up, and goes to sleep.
  • For a long time there has been an option in nodelet settings not to show the chatbox topic. As a radical break with tradition, if you select it, the chatbox topic will not be shown.
  • The revised chatterbox code only needed a little revision to be used to deal with forms in nodelets in general. So now submision of nearly all forms in nodelets will lead only to the nodelet being reloaded, not the whole page. (The exceptions are the log-in form and forms whose whole purpose is to take you to another page.) For example, you can change the number of writeups shown in New Writeups without reloading the whole page.
  • If you are an admin you have another option in New Writeups. This is now managed in the same place as the choice of length.
  • Strange things happened to the section collapsers in some nodelets when they were updated with AJAX. This turned out to be because the Ajax Update node is a node of type fullpage, and fullpage nodes are sent through the parseLinks function, which turns things in [square brackets] into links. It is now possible for fullpage nodes to say they can deal with their own links, thank-you very much.
  • The chatterbox was AJAXed, but sending a writeup message still provoked a full pageload. Solution: a method of flagging that a form field should be used as the basis for an AJAX update, not a page load. This could then be recycled and extended to links and then to arbitrary elements to provide asyncronous handling of all sorts of actions: cooling of writeups, bookmarking, adding to categories, making users 'favourite!'... The code requires no more than a little gobbledigook in the class attribute of an HTML element to ajaxify it, so there will be a lot more of this going on going forward (as we say these days). Saves your time, the E2 servers' time, and U. of Mich.'s bandwidth. Activate AJAX now, before we do it for you!
More or less unrelated to the above stream of free association football boots the chemistry set and mathchmaker match made in hell for leather shoe soul kitchen table and chair, no bill:
  • A principle of Good Web Design is that no function of a site should rely entirely on client-side scripting (code that runs in your browser): for the case that your browser doesn't Do scripts, it should also provide the same functions 100% server-side. E2 does not always comply with this. One place it didn't was in C!ing writeups when 'cool safety' was engaged: a pop-up window appeared asking for confirmation. Unless you didn't have scripting available in your browser, in which case nothing happened... To deal with this in a generalisable way there is now code in place that will ask for confirmation of an action with a pop-up box if scripting is available, or ask for confirmation by clicking on a button provided for this purpose after a page-load if not.
  • Oolong had coded an option to hide low-reputation nodes automatically in the New Writeups nodelet. These nodelets are now also hidden in e2nodes unless you ask to see them. You can decide for yourself how 'low' is low.
  • To err is human, to get driven completely and unreasonably NUTS by the trivial failings of others even more so. To make it easier for you to avoid contact with the works of those you cannot tolerate at your current level of spiritual development, there is a link from Your Ignore List to the Pit of Abomination. But only if you are already ignoring someone.
  • The New Writeups Atom Feed is now entirely proof against being truncated: if it comes out too long, a shorter version is made.
  • All of this coding business would happen much more slowly if at all if we didn't have a development server to play with and break things on where they won't get in the way. It would be nice if more members of edev were doing so. To make things even more fun and risky it is now possible to edit a code patch and apply it in one operation. This would be a Bad Idea on the production server: if we have to mess around with code here rather than importing it from the development server after fixing it so nothing explodes, then we should be forced to think about it carefully. But it would be a Bad Thing if the code on the development server were different from the production server in the long term. To make it possible to keep the code the same on the two servers while taking account of the need for different behaviour there is now a flag in System Settings, accessible to code as $HTMLVARS{isDevServer}, which is set on one server and not on the other.

This log was written on a plane. The oxygen pressure on planes is only 60% of that at standard temperature and pressure, and you should therefore avoid doing anything requiring significant mental acuity whilst flying.

Happy New Year. There has been a lot going on. This is only what's my fault:

Visible changes

  1. New page header providing operations to perform on the whole page (bookmark, add to category, add to usergroup, editor cool).
  2. Additional functions added to writeup footers: add to category; for editors: hide/unhide writeup. Add to usergroup function improved. The full list of C!s is available whether a writeup is displayed on its own page or in an e2node.
  3. Said functions removed from the epicenter nodelet/zen epicenter.
  4. All writeups which can't be voted on are flagged 'unvotable'. The reason why they are unvotable appears in a tool tip on mouse hover.
  5. 'marked for destruction' notice removed (redundant: see previous).
  6. Many of these functions are contained in pop-up widgets and are hidden when not in use to save space. They open and close at a click. When scripting is not available, the widget-openers link to a version of the page with an opened widget.
  7. Pages and writeups that are in a user's E2 bookmarks have a note indicating this with a link to the bookmark list (currently the user's homenode, but this may change).
  8. Users can choose which usergroup pages they want to have the option of adding to at a page provided for this purpose, rather than being required to guess the function of 'hideify!' and 'showify!' links. There is a link to this page next to the menu it affects.
  9. Users can simply adjust what is shown in writeup headers and footers at Writeup Settings without completely removing any more refined settings they may have entered at Old Writeup Settings.
  10. Jukka users are missing some functions. They are encouraged to switch to the Zen theme using Jukka emulation and note anything that could be improved with Jukka Emulation to improve their E2 experience at Suggestions for E2. We have enough time to make that work; keeping Jukka itself working would take too much time.

Rationale and history: tidy up and improve the writeup footer.

Writeup footers give readers the opportunity to do something in response to a writeup: say they like it, hate it, or love it, talk to the author about it, bookmark it on a social network, or whatever. Editors can also delete it, protect it from deletion, or remove the protection.

Sometimes people may also want to bookmark a writeup, add it to a usergroup page, or add it to a category. Until this month they had to look elsewhere on the page to find the means to do these things. In the case of categories, they would not have found them. Editors wishing to hide a writeup from the New Writeups list had to turn to the list itself to find the links they needed. The plan was to put all of these functions together in the writeup footer.

The links to add bookmark writeups or add them to usergroups (those 'ify!' links) were in the epicenter nodelet. Obviously, if they were to be moved out of here for writeups, it would be inconsistent to leave them there for everything else. They moved to the top of the page underneath the page title, where they were joined by the other 'full page' functions from the epicenter (favorite noder, editor cool), which would have been lonely without them. Means were also provided to remove the actions for particular nodes or nodetypes. These 'page actions' were first made visible to editors for a transitional period, to allow them to use this function.

The writeup footer was already rather cluttered, particularly for editors. But editor functions are used relatively seldom, so it made sense to hide them when they were not needed. The link used to reveal them evolved into a flag showing the status of the writeup (Public, Hidden, X'ed, Restored, Insured) under the influence of articulate feedback from the users, and acquired those triangle thingies™ to show you where to click. Various improvements and theoretically redundant changes were made so that the widgets would be visible and on the right continent even in minority Scandinavian browsers and would not jump out of the way when tickled in smug shiny browsers.

Once the complaints had died down, enthusiastic suggestions were made to put many more functions, possibly all of them, into the widget. The argument prevailed that since the point of the widget was to save space, it made sense to have something to save space for. For those who wanted the old look to their killing tools, an opt-out was provided at Writeup Settings. Unfortunately, that page destroyed any old-fashioned header/footer options they might have had even if that was all that they changed, so that had to be fixed as well.

Since a writeup is a writeup wherever it is, there seemed to be no good reason to show its C!s differently depending on whether it was alone or in company. But a long C! list takes up space and can uglify your whole footer, so it was hidden in a widget.

The existing usergroup menu was enigmatic and dependent on javascript. It was replaced by a form that works with or without scripting and that tells you what you are doing. This reduces the likelihood of people adding things to usergroups by mistake, but if they do they are informed of what they have done and are given the option of undoing it.

The usergroup form was tidied away in a pop-up widget. Following a suggestion from Oolong, this widget also contains a bookmarking link and a form to add a writeup to a category. Categories are a much underused feature of E2, partly due to their almost complete invisibility. Now that they are more visible, it is to be hoped that the users will clamour for for more and better category functionality, and coders will enthusiastically respond. The results of this could include:

  • Tools to reorder the nodes in a category.
  • Options to show all or a part of the content of all of the nodes in a category on a single page.
  • Automatic provision of links at the bottom of a writeup to the series of writeups it belongs to (a category), and to the preceding and following writeups in the sequence.
  • Your idea goes here

At the top of the page, there is more room, so bookmarking, categories and usergroup page functions are shown separately.

Back-end changes to make it all work

(Most links here will only be of use to edev members and staff.)

  • basesheet provides basic styling for widgets.
  • default javascript gives the widgets their pop-up-ness.
  • voteit provides pop-up killing widget if its parameter & 4.
  • The new htmlcode widget will build you a widget if you ask nicely.
  • openform can take a parameter hash as arguments to put style and class attributes on forms.
  • Slight tweak to async voting to adapt to new voteit markup and make it possible to pass a writeup id as argument for a quick kill.
  • displaywriteupinfo changed to make voteit return a kill widget if not disabled by the user and to include the new Add to… widget.
  • Writeup Settings checks existing settings against a regex to see if it could have generated them. Provides explicit option to replace them if not. Only adds/removes individual options if this option is not checked.
  • page header provides a page header.
  • page actions provides a list of links and widgets to put in a page header.
  • Disable actions allows staff to remove action links from the page header.
  • disabled actions keeps track of which actions are disabled for what.
  • removeweblog no longer allows (and requires!) a user id as a parameter to remove a document from a usergroup page: access privileges depend on who the user is, not on which parameters they can hack. The user who linked a document can also remove it.
  • Epicenter contains information where the links used to be on where they can be found and how to put them there if they aren't there.

Other changes/fixes

The New Writeups Atom feed has been optimised to avoid it being truncated. The fix is not 100% bombproof: theoretically, under some very unlikely conditions, it could once again fail. A few more lines of code checking the length and trying again with shorter writeup exerpts if it is over 65000 are still needed.

Writeups are now displayed by a simple htmlcode that uses the show content htmlcode to do the heavy lifting.

CoolUnCoolIt shows C!s on writeups restored from Node Row.

Node Backup will not produce corrupted zip files if your writeup titles contain characters that are not acceptable in operating systems produced in Redmond, Cupertino, or All Over the Place.

Patch importer will still complain if you reload it with a patch id still in the url, but will then go on to show the patch list.

See the top part of the table here for what I've left out above.

Purpose

To give the user an opportunity to confirm that they really want to do what the link they just clicked on/the form they just submitted is meant to do.

Implementation

1. For confirmation of opcodes, default javascript looks for links with class 'action' and parameter 'confirmop' in the href. When such a link is clicked on, the user is asked "Really <link title>?" If they confirm, the link is called (by AJAX where possible and desired) with the 'confirmop' parameter's name changed to 'op'.

2. For confirmation of actions carried out other than through opcodes, two parameters are required: confirmop=<value> and notanop=<name>. On confirmation they are removed from the query string and the parameter <name>=<value> is inserted before the link is called.

If the link is clicked and javascript is not available, page header calls confirmop to insert a callout into the page that is then loaded asking for confirmation of the action. It can ask informative questions about operations it knows about, so update it to let it know more as required.

Examples

<a href="/user/DonJaime/writeups/Viking+Toilet?cool_id=2009408&confirmop=cool" class="action ..." title="C! DonJaime's writeup">C?</a>

<a href="/title/Viking+Toilet?confirmop=hellyeah&notanop=uncoolme" title="uncool this node">uncool</a>

"CONTAINED_STUFF" is a bit of a special string within ecore. The container system that's used to generate the nodelets and things on every page, regardless of the page's content, replaces the first instance of the string 'CONTAINED_STUFF' in the container HTML with what's actually contained... normally, that's the writeup.

However, this process is applied recursively, beginning with the containing container for the htmlpage for the requested note, and following each container's 'parent container'. Usually, htmlpages specify the general container as their parent. This in turn is contained within the main container (or a variation as specified by a theme), which is in turn contained within the stdcontainer, which has no parent.

So, first stdcontainer is evaluated. Then main container is evaluated, and the text 'CONTAINED_STUFF' in the result of stdcontainer is replaced with the result of main container. This is repeated for main container and general container.

If the text 'CONTAINED_STUFF' appears more than once in the result of the container, the first one is usually the only one substituted. Usually 'CONTAINED_STUFF' is written into the source for the container. However, if it appears more than once, for example because it's a node title (which the main container will show), the wrong instance may get the contained text.

With hilarious consequences. Until someone fixes the bug.


Edit, 9th May 2009: the fun is over. It is fixed. *sniff*.

This is intended to be a quick guide in order to allow edevites to work with the currently running ecore and submit patches via Mercurial, hereafter simply "hg". As of this noding, the patches can go to me, but hopefully someone else can undertake this task later on as they become more comfortable with hg. This is also not meant to be a definitive guide. There's a whole book for that. This is just a few quick pointers to get you started.

First, make sure you understand what hg is and does.

The next step is to get it installed on your system so that you can work with it. In an apt-based system like Debian or Ubuntu, this should be as easy as "aptitude install mercurial" (make sure your sources are up to date beforehand). For Windows and MacOS X, you can download pre-built binaries here. Since hg is a primarily command-line driven program, you might find some comfort in the available GUI tools. TortoiseHG might be worth checking out. If you feel like experimenting, hgview also looks nice.

Once you're setup, you should modify your .hgrc (Unix-like) or Mercurial.ini (Windows) to identify yourself. Create the file if it doesn't exist, and add two lines like this:

  [ui]
  username = Bluepill Name <email@domain.com>
It's my habit when working on free projects' source trees to not use pseudonyms, but if you prefer to use a pseudonym instead of your bluepill name, go ahead. That name will identify the patches you submit to ecore.

In what follows I will describe the concepts from a purely command line interface point of view. The concepts are what are important; if typing at a prompt makes you uncomfortable, I imagine that TortoiseHG must have graphical methods to accomplish the same, but the important thing is to first get a basic understanding of how source control works.

The first thing you should do is get a local copy of the ecore repository on which you can work on with your locally installed tools. For this you have to clone from the ecore repository like so

hg clone http://devel.everything2.com/ecore
This will create a local directory named "ecore" from wherever you run this command that will have a snapshot of the source tree and all of its history up until that point. I should remark that ecore isn't very big, so this cloning should be really fast. If using TortoiseHG or another graphical frontend, you can run it from this source tree to get the directed acyclic graph representation of the repository up until this time. From the command line
hg log
will give dump you with the entire changelog of the project since I first committed after removing hardcoded passwords from the code. From a Unix-like system, you might want to pipe that to less so that you can read it (hg log | less) or on any system, you might want to just see a range of revisions, say from the 20th to the 30th revision, with hg log -r 20:30. Incidentally, all hg commands are documented with "help" so hg help log and hg help clone will give you more information about these commands. On most any modern installation of bash (which I believe all of MacOS X, Debian, and Ubuntu have) typing hg and hitting <TAB> will give you the list of all hg commands available which you can peruse with hg help $command.

Now that you're setup, your basic workflow will be like this,

  1. Update your working copy in case that that there have been changes since you last downloaded it with hg pull -u, which pulls from the remote repository and simultaneously updates your current copy with those changes it pulls.

  2. Make your changes on your copy, using your IDE or editor of choice.

  3. Review those changes either by the excellent integration that your editor has with hg, or by doing hg status to see which files you've modified and hg diff to get a thorough description of the changes you've made. In a Unix-like system you may want to pipe the result of these two operations to less so that you can actually read them.

  4. Commit your changes locally with hg commit -m "your commit message".

  5. If you have been given a user and password with which to push changes back into the main ecore repository, do hg push to do so, which will prompt you for your credentials. For now, I can give you a username and password with which you can push back changes. You can only commit changes, but not make them take effect; someone with shell access to the server still has to do that for you, so there's no real danger of giving widespread commit access.

    If you don't have or want a username and password for committing, you can export your patch with hg export -o some.patch tip assuming you only committed once, or hg export $r1 $r2 $r3 -o some.patch where $r1, $r2, $r3... are the revision numbers of your local changes (displayed by hg log or your GUI). Then email me or whomever's currently able to apply patches the file some.patch for inclusion into the main source tree.

That's basic usage and should be enough for starters. To summarise, clone first, then later (1) update, (2) modify, (3) review, (4) commit, (5) submit. I don't think for a quickstart you need to know too much about how to make branches and merge. You can read about that in the official book. For your own benefit and to ease your workflow, though, I am going to describe two other useful features, cloning and updating.

Cloning is something you do at the very beginning when working with a new repository, but it's something you can keep on doing once you have a local repository. The syntax is hg clone original_repo new_repo and all it does is create a new repository named new_repo that knows it was cloned from original_repo. This can be useful for a quick testing ground in case that you don't want to pollute your original repository tree with mistakes and invalid changes, and once you think you're done, you can move the changes back to the original one by running hg push from new_repo's root directory. If you have commit access to ecore, you can push your changes all the way back to e2.

Updating is being able to go back to any previous working revision of your code. You create revisions each time you run hg commit. You can see the revision number that you might want to go back to with hg log; in the changeset line, it's the number in front of the colon and before the gobbledygook (actually, a SHA-1 hash). When you do hg up (shorthand for hg update) this brings you to the latest revision, but you can go to any other revision in your changelog with hg up -r $revision_number. Note that if you're in an older revision, make changes, and commit those changes, you will have branched off at that revision, which in and of itself is ok. You'll still be able to export patches or push, but then that might involve a merge when committing those changes upstream. If you export the patch, then whoever applies the patch will be in charge of handling the merge; if you push yourself, then you'll be in charge of that merge. Try not to branch at first and keep the whole operation simple; think of updating to a previous revision just convenience for you so that you can see what the code looked like in a previous stage. If you forget at which revision you updated to, you can do hg id -n which will give you revision number at which you currently are.

That will do for now. I look forward to receiving your patches. Happy hacking!

Venerable members of this group:

Oolong@*+, dann@*, nate@*, N-Wing*, gnarl, bol, Teiresias, edev Bot, Orange Julius, rdude, call*, ascorbic@*+, Vice_hkpnx, gate, ReiToei, in10se*, elem_125, Devon, dafydd, opteek, Two Sheds*, Hexter, nosce, weivrorrim, OldMiner*, enth, Major General Panic, The Lush, timgoh0, craze, eien_meru$, rootbeer277@*+, BlackPawn, RoguePoet, redbaker, Redalien, maxClimb, randrews, Apollyon, DTal, DonJaime*, Gryffon, sam512, themanwho, resiak, visudo, raincomplex, albinowax, No Springs, loughes, WaldemarExkul, prole, moosemanmoo+, Rapscallion, alex@, DarkDigitalDream, gitm, quadricode, jdporter, Simulacron3, rycerice, kthejoker*, Sc0ut
This group of 63 members is led by Oolong@*+