| 1 |
ACTIVITY LOG
|
| 2 |
============
|
| 3 |
|
| 4 |
Introduction
|
| 5 |
------------
|
| 6 |
|
| 7 |
This module is a _working_ experiment on how to create an Activity Log module
|
| 8 |
in Drupal writing as little code as possible.
|
| 9 |
|
| 10 |
The module is meant to work as glue between:
|
| 11 |
|
| 12 |
* A database structure with two simple tables
|
| 13 |
* The Views module, used to create blocks with the db structure
|
| 14 |
* The Rules module, used to intercept module's events and log things onto
|
| 15 |
the activity log tables
|
| 16 |
|
| 17 |
|
| 18 |
This module is amazingly simple and yet amazingly powerful.
|
| 19 |
Advantages of using a glue module:
|
| 20 |
|
| 21 |
* No need for a new API. A lot of modules already interface themselves
|
| 22 |
with Rules. The ones that don't... should. Modules are far more likely to
|
| 23 |
interface themselves with Rules than a custom Activity module
|
| 24 |
|
| 25 |
* The messages are very, very easy to customise. The Token module gives you
|
| 26 |
unthinkable power
|
| 27 |
|
| 28 |
* The integration with Views means that the module's abilities will grow along
|
| 29 |
with Views'
|
| 30 |
|
| 31 |
|
| 32 |
The database structure
|
| 33 |
----------------------
|
| 34 |
|
| 35 |
Here is the db structure:
|
| 36 |
|
| 37 |
activity_log
|
| 38 |
------------
|
| 39 |
|
| 40 |
+---------------------------+----------------+-----+---------+----------------+
|
| 41 |
| Field | Type | Key | Default | Extra |
|
| 42 |
+---------------------------+----------------+-----+---------+----------------+
|
| 43 |
| aid |int(10) unsigned| PRI | NULL | auto_increment |
|
| 44 |
| uid_creator |int(10) unsigned| MUL | 0 | |
|
| 45 |
| verb |varchar(35) | MUL | | |
|
| 46 |
| verb_you |varchar(35) | | | |
|
| 47 |
| action |varchar(255) | MUL | | |
|
| 48 |
| action_multiple |varchar(255) | | | |
|
| 49 |
| action_multiple_separator |varchar(25) | | | |
|
| 50 |
| activity_timestamp |int(10) unsigned| MUL | 0 | |
|
| 51 |
+---------------------------+----------------+-----+---------+----------------+
|
| 52 |
|
| 53 |
activity_log_targets
|
| 54 |
--------------------
|
| 55 |
|
| 56 |
+------------------+------------------+------+-----+---------+----------------+
|
| 57 |
| Field | Type | Null | Key | Default | Extra |
|
| 58 |
+------------------+------------------+------+-----+---------+----------------+
|
| 59 |
| atid | int(10) unsigned | NO | PRI | NULL | auto_increment |
|
| 60 |
| aid | int(10) unsigned | NO | MUL | 0 | |
|
| 61 |
| uid_target | int(10) unsigned | NO | MUL | 0 | |
|
| 62 |
| oid_target_type | varchar(10) | NO | MUL | | |
|
| 63 |
| oid_target | int(10) unsigned | NO | MUL | NULL | |
|
| 64 |
| target_timestamp | int(10) unsigned | NO | MUL | 0 | |
|
| 65 |
+------------------+------------------+------+-----+---------+----------------+
|
| 66 |
|
| 67 |
Here is a breakdown of the fields of "activity log":
|
| 68 |
|
| 69 |
* aid - Just an auto_increment id
|
| 70 |
* uid_creator - The user ID of the user creating the action
|
| 71 |
* verb, verb_you - It's the "verb" of the action. For example "has viewed".
|
| 72 |
Note that there is a special verb if the user is watching him/herself (for
|
| 73 |
example, "have viewed"
|
| 74 |
|
| 75 |
* action, action_multiple, action_multiple_separator - It's the action side of
|
| 76 |
the story. Whereas "action" is the main one, there is an "action multiple"
|
| 77 |
part which is applied of the action has more than one target. For example,
|
| 78 |
"action" could be "the node" whereas "action_multiple" could be
|
| 79 |
"the following nodes: ". Finally, "action_multiple_separator" is the
|
| 80 |
separator used between each target.
|
| 81 |
|
| 82 |
* activity_timestamp - When the activity was added
|
| 83 |
|
| 84 |
Note that this gives a fair amount of flexibility in terms of how lines
|
| 85 |
can be composed. For example:
|
| 86 |
|
| 87 |
- merc has viewed the node "Example blog entry"
|
| 88 |
- you have viewed the following nodes: "Example blog entry", "Another", "Me"
|
| 89 |
|
| 90 |
While this may be considered English-centric, this will work with most
|
| 91 |
western languages. If it doesn't work for some, then users can change the
|
| 92 |
function theme_activity_log_line() so that it puts things "right".
|
| 93 |
|
| 94 |
|
| 95 |
Here is a breakdown of the fields of "activity log":
|
| 96 |
|
| 97 |
* atid - The activity target ID
|
| 98 |
|
| 99 |
* aid - The activity ID this target is referring to
|
| 100 |
|
| 101 |
* uid_target - The user which will be affected by the activity. This might not
|
| 102 |
be defined at all
|
| 103 |
|
| 104 |
* oid_target_type - The type of object pointed by oid_target (the following
|
| 105 |
record). For now, it can be "comment", "node", "user".
|
| 106 |
|
| 107 |
* oid_target - The object that got worked on
|
| 108 |
|
| 109 |
* target_timestamp - When the target was added
|
| 110 |
|
| 111 |
|
| 112 |
How things are added to the database
|
| 113 |
------------------------------------
|
| 114 |
Logging is done through the function rules_action_activity_log($settings);
|
| 115 |
This function is tricky.
|
| 116 |
While designing the whole thing, I figured out that I wanted this function
|
| 117 |
to do two things:
|
| 118 |
|
| 119 |
1) Make sure that the same activity is not repeated
|
| 120 |
|
| 121 |
2) Make sure that if the same activity is repeated on 5000 different
|
| 122 |
objects, the system doesn't end up with ONE line in the activity_log
|
| 123 |
table and 5000 lines in the activity_log_targets table. Which would be
|
| 124 |
_really_ fun to render (not).
|
| 125 |
|
| 126 |
|
| 127 |
This means that it's up to this function to do things right: it won't log
|
| 128 |
the same event twice unless the second one is newer by X seconds (configurable),
|
| 129 |
and it won't group more than N targets together (configurable). If there are
|
| 130 |
more than N targets, a new activity_log line is created.
|
| 131 |
|
| 132 |
A note on the design
|
| 133 |
--------------------
|
| 134 |
Talking to other developers, some of them seem to think that the "action"
|
| 135 |
itself should be meta-information, rather than (tranlsatable) English.
|
| 136 |
However, doing something like that would:
|
| 137 |
|
| 138 |
* Create another level of complication in the module
|
| 139 |
* Make it harder to add custom messages
|
| 140 |
|
| 141 |
I mean, we could aggregate "verb" and "action" into a meta-description like
|
| 142 |
"NODE_VIEW". However, this "NODE_VIEW" would then need to be configured. And
|
| 143 |
would need to be a multi-lingual configuration. And it would need to take into
|
| 144 |
consideration who is watching the entry, to have "You" there (for example). And
|
| 145 |
it should take into consideration the fact that there could be one target or
|
| 146 |
multiple targets. There should be a way (hook?) to give Rules a list of
|
| 147 |
available "activity strings". And oh, did I mention that it should be
|
| 148 |
multilingual?
|
| 149 |
I think doing that would be a _huge_ overkill. Why not simply use the power of
|
| 150 |
t() and the theme_ hooks in Drupal to deal with pretty much anything that
|
| 151 |
could possibly come up?
|
| 152 |
A message is a message is a message. to me, the couple "verb" + "action" ARE
|
| 153 |
the message description. If you need something _very_ waky to happen (for
|
| 154 |
example, have a pair "verb" + "action" for which you want a completely
|
| 155 |
different rendering for), then you can use the theming functions. But it's
|
| 156 |
such a remote case, that it wouldn't be beneficial to change the whole
|
| 157 |
structure just for that. "Keep it simple stupid".
|
| 158 |
Of course, please feel free to prove me wrong. I might well be. However, I
|
| 159 |
have tortured myself with Facebook for moree than 2 hours just now, and
|
| 160 |
came to the conclusion that they must be following a structure similar to
|
| 161 |
what I have come up with for their messages.
|
| 162 |
|
| 163 |
|
| 164 |
Why a generic module won't cut it
|
| 165 |
---------------------------------
|
| 166 |
People asked me why I didn't just port the "system log" event module from
|
| 167 |
workflow_ng to to exactly this. The answer is "Because it's not enough". We
|
| 168 |
don't need to know what events are generated. We need to know who generated
|
| 169 |
what and who and what was affected by it.
|
| 170 |
Then, it would be very convenient if the "grouping" was done for us (good
|
| 171 |
luck writing the queries for presentation otherwise).
|
| 172 |
|
| 173 |
So, that's why. Of course, feel free to prove me wrong. If you manage to
|
| 174 |
generate the kind of output that Activity Log generates (with grouping of
|
| 175 |
maximum N items, avoiding repeated logging, etc.), then it means that this
|
| 176 |
module is useless.
|
| 177 |
|
| 178 |
|
| 179 |
Rules integration
|
| 180 |
-----------------
|
| 181 |
Rules integration seems to be complete.
|
| 182 |
|
| 183 |
|
| 184 |
Views integration
|
| 185 |
-----------------
|
| 186 |
Views integration is _incomplete_. I have basically put the basic table
|
| 187 |
descriptions out there. However, I am not sure how to get Views to do
|
| 188 |
what we need.
|
| 189 |
|
| 190 |
What I *DID* do, is to clarify VERY precisely what we need: I have created a
|
| 191 |
sample query function, 4 configurable blocks, and some theme functions. This
|
| 192 |
will allow us to see "what we should get Views to do" -- and it will also
|
| 193 |
allow people to use the module IF Views "can't do it" (which I doubt) or
|
| 194 |
IF a user wants to use this without Views (which is possible).
|
| 195 |
|
| 196 |
Everything is themable "the Drupal way". However, I am not sure if the theme
|
| 197 |
functions should be used by Views, for example.
|
| 198 |
|
| 199 |
Conclusion
|
| 200 |
----------
|
| 201 |
Well, that's it. IF I got it right, IF this solution/structure doesn't have
|
| 202 |
too many limitations, IF Views can be configured so that it works 100% OK,
|
| 203 |
then we have a glue module for actions.
|
| 204 |
|
| 205 |
|
| 206 |
|
| 207 |
|
| 208 |
|
| 209 |
|
| 210 |
|