First of all, you may want to begin by reading the previous two
entries in this series by kthejoker: Coding for E2: A primer and
Coding for E2: E2 Syntax. You might also be interested in first
reading edev: Quickstart. In this entry, I'm going to try to guide
you through some examples of first finding where the code
that you may want to change is, so that at least you can get started
with an idea of what to do.
Finding the code in e2 that affects whatever functionality you're
interesting in changing can be difficult. Broadly, the code can be in
one of five places:
- Nodes
- The server's current Everything Engine, hereafter abbreviated to
"ecore"
- Standard Perl libraries like CGI.pm
- Standard Javascript libraries like Scriptaculous
- Perl cron jobs running in the server
Thankfully, most of the code that you will want to see and modify is
readily available to you in a node somewhere, but just
finding which node can be a challenge. The running ecore is
accessible from Gigantic Code Lister, along with other bits of code
like htmlcodes (currently only available to admins; trying
to change this ASAP so that edevites can participate too). External
libraries like CGI.pm or Scriptaculous are found in external
sites. Our global variable to access the CGI.pm object
is $query; so you'll see that running around the code now and
then for creating form elements. Lastly, the only way right now to
look at the cron jobs is to have ssh access into the server. I would
like to change this soon so that edevites really have read access to
all the code they need in order to make e2 better. We'll see.
But like I said, most of the code is in nodes and accessible to
edevites as long as the nodetype is accessible (e.g.
restricted_superdoc is a nodetype that is only accessible to
admins). You can see who has access to a particular nodetype by going
to the nodetype node ("everything's a node", remember?). The codeful nodes can be
further broken down into the following main categories:
superdoc and slight variations of it (superdocnolinks,
restricted_superdoc, oppressor_superdoc) — examples: Message Inbox, Everything User
Search.
htmlcode — examples: sendPrivateMessage, zenDisplayUserInfo
nodelet – examples: Epicenter, Other Users.
htmlpage — examples: e2node display page,
debatecomment edit page
container — examples: zen stdcontainer, general container
opcode — examples: message, cool
dbtable — examples: node, writeup
maintenance — examples: writeup maintenance update,
patch create maint.
jscript — examples async voting, Everything2 ajax
Smaller pieces of code like notification and achievement
nodetypes.
You can go to List Nodes of Type in order to get the full list of
each nodetype if you wish. Each of those nodetypes requires an
explanation of what they are for, what kind of code it houses, and how
do you find it.
The superdocs and their variations are nodes that display by
themselves and often perform some kind of code. When you visit them,
you'll see that they often render some kind of content. Let's look at
a particularly simple example, the Fezisms Generator. Go to the
superdoc node and hit viewcode in your edev nodelet.
As explained in the last article in this
series, you'll see some raw HTML which isn't filtered in superdocs
like it is in writeups and discussion posts. Immediately you'll see
the magic "[%" signaling "Perl code begins now" and some
simple Perl code that defines two arrays of things thefez might say.
The Perl block of code ends with the symmetrical "%]"
There are two blocks of Perl code here, and these blocks are
essentially Perl functions: e.g. they can return values like
any Perl function, or will return the last value in them when parsing
through them. In this case, the Fezisms Generator returns two random
chunks from each of the @wit arrays in each block of Perl code and
that's all, creating enduring hilarity for the
ages.
The other nodetypes of the superdoc family have differences in how
they handle the filtering of the HTML (for example, superdocnolinks
doesn't call the ecore function parseLinks , so links in brackets aren't rendered) or the
permissions associated to them. Scratch Pads is a prominent example
of a node that for whatever reason is of nodetype superdocnolinks and not
superdoc, but for practical purposes in terms of code is pretty much
the same. More interesting examples of superdocs could be user
settings and nodelet settings or even the
frontpage.
The htmlcode nodetype is an interesting and sometimes frustrating
kind of code in e2. Its original purpose as its name indicates was
clearly to generate boilerplate HTML within certain nodes. For
example, for e2nodes (writeups containers), we have in
e2node display page the firmlink, softlink, and addwriteup
htmlcodes being called with the [{some_htmlcode}]
syntax as described in a previous writeup.
However, because htmlcodes can also be called a inside Perl blocks
with ecore's htmlcode function, their original purpose has
been perverted to be used as "general purpose functions" and using
their return "HTML" as a pure string or scalar with which Perl can do
other things. Examples of this usage of htmlcodes are rootbeer277's
dragUsers or kthejoker's isinUsergroup.
Ecore's htmlcode function for calling htmlcodes deserves
special attention, since the way it works is downright barbaric in our
running 0.8ish ecore. Its non-intuitive functioning has been a source
of frustration and has caused bugs in the past, even
as recently as this year when trying to apply call's table
screening code as an htmlcode (it was impossible as an htmlcode and we
had to resort to jamming call's code into ecore in the server).
Our running ecore's htmlcode code function takes at most two
and no more than two arguments: a Perl string with the name of the
htmlcode to call and another string with its arguments. It then
performs a very simple parsing in a Perlish way to split the
second argument by commas, and the resulting array will get passed to
the called htmlcode's argument array @_. From the called
htmlcode, you can then do "my ($foo,$bar,$baz) = @_" to recover the
three arguments that ecore passed to this htmlcode.
The problem is that the arguments have to be extremely simple, since
ecore performs a very low-level split on commas alone. This means, for
example, that you cannot pass a writeup text to an htmlcode as we
wanted to do with the screenTable function, because writeups almost
certainly contain commas in them, and ecore will interpret that as
different arguments. Another example is that JSON objects are
strings that contain commas in them, so you'll see for instance in
debate maintenance create that the notification code has to escape
the commas in the JSON which the addNotificaiton htmlcode has to
unescape and split upon.
There is one further bit of complication with passing arguments to
htmlcodes this way and that's that ecore essentially does string
parsing and macro substitutions when passing arguments. What this
means is that quotation marks are not escaped either, so it's
difficult or impossible to pass any arguments to an htmlcode that
contains quotes, because then Perl will be unable to parse the
resulting string that ecore's htmlcode creates.
Despite these limitations, as long as one stays aware of them and is
careful about the argument string that goes into an htmlcode,
htmlcodes can be very useful to have certain functions within the
database instead of implemented in the server.
This is also pretty straightforward: because everything is a
node, nodelets are also nodes, so they can be seen like ordinary
nodes in addition to displaying in your sidebar. The kind of code inside
nodelets is pretty much the same as the code for superdocs; you can
also put unescaped HTML inside nodelets and Perl blocks are also
signaled in the same way.
I do want to mention one peculiarity of nodelets and that is
nodelet sections, which are sometimes a bit difficult to
find. Nodelet sections are those parts of a nodelet that you can
collapse with the little + and - buttons. Let's look at an
example, the Statistics nodelet. When you look at this nodelet's
source, you'll see that it's a rather terse bit of code that only
calls the nodeletsection htmlcode with some parameters several
times. After parsing N-Wing's code in nodeletsection, you can
eventually figure out that what it's doing is concatenating the first
two parameters passed to the htmlcode in order to invoke yet another
htmlcode. So for example, the first section in the Statistics Nodelet,
"stat,personal,Yours" becomes the htmlcode statsection_personal and
the "stat,global,E2" the statsection_global htmlcode. The general
formula for finding other nodelet sections applies to other nodelets.
These are the nodetypes that are used to render nodes. The way it
works is that ecore fetches a node, sees what htmlpage goes with
this node, sees if that htmlpage has a container, if that container
has another container, and all the way up to a parent container, and
then starts rendering the page by calling the top container, replacing
the infamous CONTAINED_STUFF in the top cotainer with the next
container in the list, all the way down until it reaches the
htmlpage and voilĂ , the node is rendered, along with the nodelets
and all the goodness that is e2. This chain is also determined by the
current theme you're using, Jukka, classic, or Zen.
You can see which htmlpage is being used to render the node you're
looking at because it will be in your edev nodelet. If for example
you want to add a new text field to the homenode edit page, you can go
to your homenode, edit it, look at your edev nodelet and observe that
classic user edit page is the htmlpage that it's using. This is what
you would have to patch to add another textfield to the homenode edit screen.
Not all htmlpages have containers, though. For example, the fullpage
display page htmlpage is very simple and has no container, the
jscript nodetype is another example that has a jscript display
page without a container. Chatterlight is an example of a node of
type fullpage, and if you look at its code (put
"&displaytype=viewcode" in the query string in the address bar of your
browser when visiting chatterlight), you'll see that chatterlight
does a lot of work of its own to render the page without relying on
the default formatting that containers provide.
In particular, most of the site-wide CSS for the Zen Theme are
provided in zen container and zen stdcontainer. This is where you
can find a good chunk of the work that ascorbic and kthejoker did
to bring e2 out of the dark ages of table-based layout and into a new
era of CSS goodness.
An opcode is a bit of code for e2 that performs certain actions; it's
called in conjuction with other bits of code. The way to call an
opcode is to pass the "op=$opcode_name" CGI parameter to e2 somehow,
either by a query string in a GET method or by POST. For example, if
you look at chatterlight's rendered HTML source in your browser,
you'll find that there is a hidden CGI form element
<input type="hidden" name="op" value="message" />
which means, "when this form is submitted, call the message opcode",
and is exactly what will happen. Most things you think of as
operations in e2, voting, cooling, sending /msgs, are opcodes. It's
instructive to go to List Nodes of Type and browse through the
opcodes that we have available.
This is the nodetype that ecore uses for representing tables in the
underlying MySQL table that houses all of our data. It isn't really a nodetype that contains
code except in the sense that all the of e2's data is ultimately in
dbtables, and if it's data in a node somewhere, like much of the code
is, then it's in a dbtable.
You will often see ecore calls of type $DB->sql* which will
invariably mention a dbtable in their first or second argument. If you
ever need to refer to the structure of the underlying dbtable, go look
at its node, which looks very much like any other MySQL table
description, telling you what columns the table and their type.
Unfortunately, unless you actually look at the data in the columns,
it's not always easy to guess what's in each column. In case of doubt,
consult an e2coder who will be able to make a quick query
on the table and report back on its contents.
Whenever a node, any node (and this includes writeups, users,
nodeshells, because everything is a node) is created,
updated, or deleted (nuked), ecore will call a
corresponding createNode, updateNode,
or deleteNode function. Each of these nodes will check in
turn if this nodetype has an associated maintenance node that will
perform those operations, and if it does (most nodes don't need that
much maintenance), it will execute the code in this maintenance
nodetypes.
When you look at a nodetype's node (e.g. the debatecomment
nodetype), you'll see listed near the bottom after it lists the
permissions any maintenance nodes associated to it, if any. For
example, debatecomment has debatecomment maintenance create which
amidst its maintenance code has the notification code for notifiying
users when a new reply has been posted to usergroup discussions.
Writeups have writeup maintenance delete which in turn contains code
for adjusting the XP and writeup count of users for when their
writeups get nuked. And so on.
This nodetype is quite simple: it's what contains javascript to be
used by e2. It has no container, so if you want to patch jscript
nodes, you'll have to manually manipulate your URL and attach
"&displaytype=viewcode" to the query string, or you can click on their
(patch) link from javascript repository. After that, zen
stdcontainer makes a direct call by node_id to Javascript compiler
to place the right javascript in the header. For now, async voting
gets embedded directly by zen stdcontainer in a separate line
without going through javascript compiler.
I should remark, this isn't the only bit of javascript in e2. We have
lots of other bits of javascript in e2 embedded directly into the code
without going through jscript nodetypes; TinyMCE is one prominent
example of one. Usually it's easy by looking at the rendered page to
guess which node has the embedded javascript when the javascript isn't
being served by a jscript nodetype, though.
Other codeful nodetypes
These aren't the only nodetypes that can hold code, but they are the
most important ones. We have minor nodetypes that only have a handful
of nodes like the notification nodetype that handles what gets
displayed in the Notifications nodelet or the achievement nodetype
that has nodes with code that determine when an achievement in My
Achievements has been... well, achieved. This should be plenty to get
you started, though.
That's all for this node. Gigantic Code Lister, currently only available to admins, lists all of the code in the
running ecore as well as optionally all the htmlcodes. Also in the
server, under /usr/local/everything/bin, you can find some
Perl scripts that cron calls to perform maintenance tasks like
moving writeups from Node Row to Node Heaven. This should be
enough to help you find your way around the code.
Happy hacking!