Elgg
Elgg
Elgg
Release master
Various
Contents
Getting Started
1.1 Features . . . . . . .
1.2 Bundled plugins . .
1.3 License . . . . . . .
1.4 Installation . . . . .
1.5 Developer Overview
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
4
13
15
21
Administrator Guides
2.1 Getting Started . . . .
2.2 Upgrading Elgg . . .
2.3 Plugins . . . . . . . .
2.4 Performance . . . . .
2.5 Cron . . . . . . . . .
2.6 Backup and Restore .
2.7 Duplicate Installation .
2.8 Getting Help . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
25
25
26
29
31
35
37
50
55
Developer Guides
3.1 Dont Modify Core . . .
3.2 Plugins . . . . . . . . .
3.3 Plugin coding guidelines
3.4 Accessibility . . . . . .
3.5 Ajax . . . . . . . . . .
3.6 Authentication . . . . .
3.7 Context . . . . . . . . .
3.8 Database . . . . . . . .
3.9 Forms + Actions . . . .
3.10 Helper functions . . . .
3.11 Internationalization . . .
3.12 JavaScript . . . . . . . .
3.13 Menus . . . . . . . . .
3.14 Notifications . . . . . .
3.15 Page handler . . . . . .
3.16 Routing . . . . . . . . .
3.17 Services . . . . . . . . .
3.18 Page ownership . . . . .
3.19 Permissions Check . . .
3.20 Plugin settings . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
59
60
71
73
75
78
80
80
87
94
95
96
103
106
111
111
113
114
114
116
3.21
3.22
3.23
3.24
3.25
3.26
3.27
3.28
3.29
3.30
4
ii
River . . . . . . . . . . . .
Routing . . . . . . . . . . .
Themes . . . . . . . . . . .
Views . . . . . . . . . . . .
Widgets . . . . . . . . . . .
Walled Garden . . . . . . .
Web services . . . . . . . .
Upgrading Plugins . . . . .
List of events in core . . . .
List of plugin hooks in core
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
117
118
120
123
133
137
137
143
164
167
Tutorials
4.1 Hello world . . . . . . . . . .
4.2 Customizing the Home Page .
4.3 Building a Blog Plugin . . . .
4.4 Integrating a Rich Text Editor
4.5 Basic Widget . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
175
175
176
176
181
182
Design Docs
5.1 Actions . . . . . . . . . .
5.2 Database . . . . . . . . .
5.3 Events and Plugin Hooks .
5.4 Internationalization . . . .
5.5 AMD . . . . . . . . . . .
5.6 Security . . . . . . . . . .
5.7 Loggable . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
185
185
185
200
204
204
205
208
Contributor Guides
6.1 Translations . . . . . . . . . . .
6.2 Reporting Issues . . . . . . . . .
6.3 Writing Code . . . . . . . . . . .
6.4 Adding a Service to Elgg . . . . .
6.5 Writing Documentation . . . . .
6.6 Internationalizing documentation
6.7 Becoming a Financial Supporter .
6.8 Release Process Workflow . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
211
211
211
212
222
223
225
226
227
Appendix
7.1 FAQs and Other Troubleshooting
7.2 Roadmap . . . . . . . . . . . . .
7.3 Release Policy . . . . . . . . . .
7.4 Support policy . . . . . . . . . .
7.5 History . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
231
231
258
260
260
261
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Elgg (pronunciation) is a rapid development framework with built-in social features. It is a great fit for building
any app where users log in and share information.
It has been used to build all kinds of social apps:
open networks (similar to Facebook)
topical (like the Elgg Community)
private intranets
dating
educational
company blog
There is also a demo site running a standard installation of Elgg.
This is the canonical documentation for the Elgg project.
Contents
Contents
CHAPTER 1
Getting Started
1.1 Features
Demo: http://demo.elgg.org/
Showcases: https://community.elgg.org/showcase
Activity stream
Plugins for common content types like blogs, bookmarks, files, microblogging, private messages, documents,
message boards, discussion
User authentication and administration
If you need more functionality than what Elgg offers out-of-the-box there are a couple of options:
Add more by installing plugins - for example, blogs, forums, social bookmarks
Develop your own features via plugins
Hire someone to do the above for you
1.2.1 Blog
A weblog, or blog, is arguably one of the fundamental DNA pieces of most types of social networking site. The
simplest form of personal publishing, it allows for text-based notes to be published in reverse-chronological order.
Commenting is also an important part of blogging, turning an individual act of publishing into a conversation.
Elggs blog expands this model by providing per-entry access controls and cross-blog tagging. You can control exactly
who can see each individual entry, as well as find other entries that people have written on similar topics. You can also
see entries written by your friends (that you have access to).
See also:
Blogging on Wikipedia
1.2.2 Dashboard
The dashboard is bundled with both the full and core-only Elgg packages. This is a users portal to activity that is
important to them both from within the site and from external sources. Using Elggs powerful widget API, it is
possible to build widgets that pull out relevant content from within an Elgg powered site as well as grab information
from third party sources such as Twitter or Flickr (providing those widgets exist). A users dashboard is not the same
as their profile, whereas the profile is for consumption by others, the dashboard is a space for users to use for their own
needs.
1.2.3 Diagnostics
For the technically savvy user, system diagnostics enables you to quickly evaluate the server environment, Elgg code,
and plugins of an Elgg install. System diagnostics is a core plugin that comes turned on by default with Elgg. To
download the diagnostics file, follow the steps below. The file is a dump of all sorts of useful information.
To use:
Log in as Administrator
Go to Administration -> Administer -> Utilities ->System diagnostics
Click Download
System diagnostics dump file contents:
4
List of all Elgg files along with a hash for each file
List of all the plugins
PHP superglobals
PHP settings
Apache settings
Elgg CONFIG values
language strings
site settings
database settings
plugin hooks
actions
views
page handlers
much more
Photo gallery
When a user uploads photographs or other pictures, they are automatically collated into an Elgg photo gallery that can
be browsed through. Users can also see pictures that their friends have uploaded, or see pictures attached to a group.
Clicking into an individual file shows a larger version of the photo.
Podcasting
An Elgg file repository RSS feed automatically doubles as an RSS feed, so you can subscribe to new audio content
using programs like iTunes. Using the zaudio plugin, supplied in the default Elgg package, MP3 audio files are also
directly playable in the page.
Special content
It is possible for other plugins to add to the players available for different content types. Its possible for a plugin
author to embed a viewer for Word documents, for example.
Note for developers
To add a special content type player,
create a plugin with views of the form
file/specialcontent/mime/type. For example, to create a special viewer for Word documents, you would
create a view called file/specialcontent/application/msword, because application/msword is
the MIME-type for Word documents. Within this view, the ElggEntity version of the file will be referenced as
$vars[entity]. Therefore, the URL of the downloadable file is:
<?php echo $vars['url']; ?>action/file/download?file_guid=<?php echo $vars['entity']->getGUID(); ?>
1.2.5 Groups
Once you have found others with similar interests - or perhaps you are part of a research groups or a course/class - you
may want to have a more structured setting to share content and discuss ideas. This is where Elggs powerful group
building can be used. You can create and moderate as many groups as you like
You can keep all group activity private to the group or you can use the make public option to disseminate work
to the wider public.
Each group produces granular RSS feeds, so it is easy to follow group developments
Each group has its own URL and profile
Each group comes with a File repository, forum, pages and messageboard
1.2.6 Messageboard
The messageboard - similar to The Wall in Facebook or a comment
wall in other networks is a plugin that lets users put a messageboard
widget on their profile. Other users can then post messages that will
appear on the messageboard. You can then reply directly to any message and view the history between yourself and the person posting the
message.
1.2.7 Messages
Private messaging can be sent to users by clicking on their avatar or
profile link, providing you have permission. Then, using the built in
WYSIWYG editor, it is possible to format the message. Each user has
their own inbox and sentbox. It is possible to be notified via email of
new messages.
When users first login, they will be notified about any new message by
the messages notification mechanism in their top toolbar.
1.2.8 Pages
on. The second thing is in the area of collaboration, especially when in the context of groups. With the powerful
access controls on both read and write, this plugin is ideal for collaborative document creation.
Note: Developers should note that there are actually 2 types of pages:
1. Top-level pages (with subtype page_top)
Fig. 1.5: Message notification
1.2.9 Profile
10
11
User avatar
The user avatar represents a user (or a group) throughout the site. By
default, this includes a context-sensitive menu that allows you to perform actions on the user it belongs to wherever you see their avatar.
For example, you can add them as a friend, send an internal message,
and more. Each plugin can add to this context menu, so its full contents will vary depending on the functionality active in the current Elgg site.
Notes for developers
Using a different profile icon To replace the profile icon, or provide more
content, extend the icon/user/default view.
Adding to the context menu The context menu can be expanded by registering a plugin hook for register menu:user_hover, the following
sections have special meaning:
default for non-active links (eg to read a blog)
admin for links accessible by administrators only
In each case, the user
$params[entity].
in
question
will
be
passed
as
12
members
notifications
reportedcontent
search
site_notifications
tagcloud
twitter_api
uservalidationbyemail
web_services
zaudio
1.3 License
1.3.1 MIT or GPLv2
A full Elgg package that includes the framework and a core set of plugins is available under version 2 of the GNU General Public License
(GPLv2). We also make the framework (without the plugins) available
under the MIT license.
1.3.2 FAQ
The following answers are provided as a convenience to you; they are
not legal counsel. Consult with a lawyer to be sure about the answers
to these questions. The Elgg Foundation cannot be held responsible
for decisions you make based on what you read on this page.
For questions not answered here, please refer to the official FAQ for
the GPLv2.
How much does Elgg cost?
Elgg is free to download, install, and use. If youd like to donate, we
do appreciate our financial supporters!
Can I remove the Elgg branding/links?
Yes.
Can I modify the source code?
Yes, but in general we recommend you make your modifications as
plugins so that when a new version of Elgg is released, the upgrade
process is as painless as possible.
1.3. License
13
14
1.4 Installation
Get your own instance of Elgg running in no time.
Contents
Requirements
Overview
Other Configurations
Troubleshooting
1.4.1 Requirements
MySQL 5+
PHP 5.5+ with the following extensions:
GD (for graphics processing)
Multibyte String support (for i18n)
Proper configuration and ability to send email through an MTA
Web server with support for URL rewriting
Official support is provided for the following configurations:
Apache server
Apache with the rewrite module enabled
PHP running as an Apache module
Nginx server
Nginx with PHP-FPM using FastCGI
By official support, we mean that:
Most development and testing is performed with these configurations
Much of the installation documentation is written assuming Apache or
Nginx is used
Priority on bug reports is given to Apache and Nginx users if the bug
is web server specific (but those are rare).
Browser support policy
Feature branches support the latest 2 versions of all major browsers as
were available at the time of the first stable release on that branch.
Bugfix release will not alter browser support, even if a new version of
the browser has since been released.
Major browsers here means all of the following, plus their mobile
counterparts:
Android Browser
1.4. Installation
15
Chrome
Firefox
IE
Safari
Support may mean that we take advantage of newer, unimplemented
technologies but provide a JavaScript polyfill for the browsers that
need it.
You may find that Elgg happens to work on unsupported browsers, but
compatibility may break at any time, even during a bugfix release.
1.4.2 Overview
Upload Elgg
With Composer (recommended if comfortable with CLI):
cd /path/to/wwwroot/
composer self-update
composer global require "fxp/composer-asset-plugin:~1.0"
composer create-project elgg/starter-project:dev-master .
16
Setting your data directory to 777 will work, but it is insecure and is
not recommended. If you are unsure how to correctly set permissions,
contact your host for more information.
17
MAMP
MariaDB
Nginx
Ubuntu
Virtual hosts
XAMPP
1.4.4 Troubleshooting
Help! Im having trouble installing Elgg
First:
Recheck that your server meets the technical requirements for Elgg.
Follow the environment-specific instructions if need be
Have you verified that mod_rewrite is being loaded?
Is the mysql apache being loaded?
Keep notes on steps that you take to fix the install. Sometimes changing some setting or file to try to fix a problem may cause some other
problem later on. If you need to start over, just delete all the files, drop
your database, and begin again.
I cant save my settings on installation (I get a 404 error
when saving settings)
Elgg relies on the mod_rewrite Apache extension in order to simulate certain URLs. For example, whenever you perform an action in
Elgg, or when you visit a users profile, the URL is translated by the
server into something Elgg understands internally. This is done using
rules defined in an .htaccess file, which is Apaches standard way
of defining extra configuration for a site.
This error suggests that the mod_rewrite rules arent being picked
up correctly. This may be for several reasons. If youre not comfortable implementing the solutions provided below, we strongly recommend that you contact your system administrator or technical support
and forward this page to them.
The .htaccess, if not generated automatically (that happens when
you have problem with mod_rewrite), you can create it by renaming install/config/htaccess.dist file you find with elgg
package to .htaccess. Also if you find a .htaccess file inside
the installation path, but you are still getting 404 error, make sure the
contents of .htaccess are same as that of install/config/htaccess.dist.
mod_rewrite isnt installed.
Check your httpd.conf to make sure that this module is being
loaded by Apache. You may have to restart Apache to get it to pick up
18
any changes in configuration. You can also use PHP info to check to
see if the module is being loaded.
The rules in .htaccess arent being obeyed.
In your virtual host configuration settings (which may be contained
within httpd.conf), change the AllowOverride setting so that it
reads:
AllowOverride all
This will tell Apache to pick up the mod_rewrite rules from
.htaccess.
Elgg is not installed in the root of your web directory (ex:
http://example.org/elgg/ instead of http://example.org/)
The install script redirects me to action when it should be
actions
This is a problem with your mod_rewrite setup. DO NOT, REPEAT, DO NOT change any directory names!
I installed in a subdirectory and my install action isnt working!
If you installed Elgg so that it is reached with an address like
http://example.org/mysite/ rather than http://example.org/, there is a
small chance that the rewrite rules in .htaccess will not be processed
correctly. This is usually due to using an alias with Apache. You may
need to give mod_rewrite a pointer to where your Elgg installation is.
Open up .htaccess in a text editor
Where
prompted,
add
a
line
like
/path/to/your/elgg/installation/
the trailing slash)
RewriteBase
(Dont
forget
1.4. Installation
19
check if it is really the elgg-created .htaccess (not only a dummy provided from the server provider)
if it is not the elgg provided htaccess file, use the htaccess_dist (rename
it to .htaccess)
I get an error message that the rewrite test failed after the
requirements check page
I get the following messages after the requirements check step (step 2)
of the install:
We think your server is running the Apache web server.
The rewrite test failed and the most likely cause is that AllowOverride is not set to All for Elggs directory. This prevents Apache from
processing the .htaccess file which contains the rewrite rules.
A less likely cause is Apache is configured with an alias for your Elgg
directory and you need to set the RewriteBase in your .htaccess. There
are further instructions in the .htaccess file in your Elgg directory.
After this error, everinteraction with the web interface results in a error
500 (Internal Server Error)
This is likely caused by not loading the filter module by uncommenting the
#LoadModule filter_module modules/mod_filter.so
line in the httpd.conf file.
the Apache error.log file will contain an entry similar to:
... .htaccess: Invalid command AddOutputFilterByType, perhaps
misspelled or defined by a module not included in the server configuration
There is a white page after I submit my database settings
Check that the Apache mysql module is installed and is being loaded.
Im getting a 404 error with a really long url
If
you
see
a
404
error
during
the
install
or
on
the
creation
of
the
first
user
with
a
url
like:
http://example.com/homepages/26/d147515119/htdocs/elgg/action/register
that means your site url is incorrect in your sites_entity table in your
database. This was set by you on the second page of the install. Elgg tries to guess the correct value but has difficulty
with shared hosting sites. Use phpMyAdmin to edit this value to the correct base url.
I am having trouble setting my data path
This is highly server specific so it is difficult to give specific advice. If
you have created a directory for uploading data, make sure your http
20
server can access it. The easiest (but least secure) way to do this is
give it permissions 777. It is better to give the web server ownership
of the directory and limit the permissions.
The top cause of this issue is PHP configured to prevent access to
most directories using open_basedir. You may want to check with
your hosting provider on this.
Make sure the path is correct and ends with a /. You can check the
path in your database in the datalists table.
If you only have ftp access to your server and created a directory but
do not know the path of it, you might be able to figure it out from
the www file path set in your datalists database table. Asking for help
from your hosting help team is recommended at this stage.
I cant validate my admin account because I dont have an
email server!
While its true that normal accounts (aside from those created from
the admin panel) require their email address to be authenticated before
they can log in, the admin account does not.
Once you have registered your first account you will be able to log in
using the credentials you have provided!
I have tried all of these suggestions and I still cannot install
Elgg
It is possible that during the process of debugging your install you
have broken something else. Try doing a clean install:
drop your elgg database
delete your data directory
delete the Elgg source files
start over
If that fails, seek the help of the Elgg community. Be sure to mention
what version of Elgg you are installing, details of your server platform,
and any error messages that you may have received including ones in
the error log of your server.
21
1.5.2 Plugins
Plugins change the behavior or appearance of Elgg by overriding
views, or by handling events and plugin hooks. All changes to an
Elgg site should be implemented through plugins to ensure upgrading
core is easy.
1.5.3 Actions
Actions are the primary way users interact with an Elgg site. Actions
are registered by plugins.
1.5.5 Views
Views are the primary presentation layer for Elgg. Views can be overridden or extended by Plugins. Views are categories into a Viewtype,
which hints at what sort of output should be expected by the view.
1.5.6 JavaScript
Elgg uses an AMD-compatible JavaScript system provided by require.js. Bundled with Elgg are jQuery 1.11.0, jQuery UI 1.10.4,
jQuery Form v20140304, jQuery jeditable, and jQuery UI Autocomplete.
Plugins can load their own JS libs.
1.5.7 Internationalization
Elggs interface supports multiple languages, and uses Transifex for
translation.
22
1.5.8 Caching
Elgg uses two caches to improve performance: a system cache and
SimpleCache.
23
24
CHAPTER 2
Administrator Guides
25
2.2.1 Advice
Back up your database and code
Mind any version-specific comments below
Upgrade only one minor version at a time (1.6 => 1.7, then 1.7 => 1.8)
Try out the new version on a test site before doing an upgrade
Report any problems in plugins to the plugin authors
If you are a plugin author you can report any backwards-compatibility issues to GitHub
26
For Nginx from install/config/nginx.dist into your server configuration (usually inside
/etc/nginx/sites-enabled)
5. Merge any new changes from settings.example.php into settings.php
6. Visit http://your-elgg-site.com/upgrade.php
Note: Any modifications should have been written within plugins, so that they are not lost on overwriting. If this is
not the case, take care to maintain your modifications.
27
.htaccess
engine/settings.php
any 3rd-party plugin folders in the mod directory
Then replace the old installation directory with the new one. This way you are guaranteed to get rid of obsolete files
which might cause problems if left behind.
Follow the basic instructions listed above.
After youve visited upgrade.php, go to the admin area of your site. You should see a notification that you have
pending upgrades. Click the link in the notification bar to view and run the upgrades.
The new notifications system delivers messages via a minutely cron handler. If you havent done so yet, you will need
to install and configure crontab on your server. If cron jobs are already configured, note that the scope of available
cron periods may have changed and you may need to update your current crontab to reflect these changes.
Time commitment
Running all of the listed upgrades took about 1 hour and 15 minutes on the Elgg community site which at the time had
to migrate:
~75,000 discussion replies
~75,000 comments
~75,000 data directories
You should take this only as a ballpark estimate for your own upgrade. How long it takes will depend on how large
your site is and how powerful your servers are.
28
Warning: If you do not delete these directories before an upgrade, you will have problems!
2.3 Plugins
Plugins can modify the behavior of and add new features to Elgg.
Contents
Where to get plugins
The Elgg Community
Finding Plugins
Evaluating Plugins
Types of plugins
Themes
Language Packs
Installation
Plugin order
Pre-1.8 notes
On the community plugin page, you can sort by date uploaded (Filter: Newest) or number of downloads (Filter: Most
downloads). Sorting by the number of downloads is a good idea if you are new to Elgg and want to see which plugins
are frequently used by other administrators. These will often (but not always) be higher quality plugins that provide
significant capabilities.
Use the plugin tag search
Next to the filtering control on the plugin page is a search box. It enables you to search by tags. Plugins authors choose
the tags.
2.3. Plugins
29
The quality of plugins varies substantially. If you find a plugin that works well on your site, you can check what else
that plugin author has developed by clicking on their name when viewing a plugin.
Evaluating Plugins
Look at the comments and ratings
Before downloading and using a plugin, it is always a good idea to read through the comments that others have left.
If you see people complaining that the plugin does not work or makes their site unstable, you probably want to stay
away from that plugin. The caveat to that is that sometimes users ignore installation instructions or incorrectly install
a plugin and then leave negative feedback. Further, some plugin authors have chosen to not allow comments.
Install on a test site
If you are trying out a plugin for the first time, it is a bad idea to install it on your production site. You should maintain
a separate test site for evaluating plugins. It is a good idea to slowly roll out new plugins to your production site even
after they pass your evaluation on your test site. This enables you to isolate problems introduced by a new plugin.
2.3.4 Installation
All plugins reside in the mod directory of your Elgg installation.
To install a new plugin:
extract (unzip) contents of the plugin distribution package
30
copy/FTP the extracted folder into the mod directory of your Elgg installation, making sure that
manifest.xml and start.php are directly under the plugin directory (e.g. if you were
to install a plugin called my_elgg_plugin, plugins manifest would need to be found at
mod/my_elgg_plugin/manifest.xml)
activate the plugin from your admin panel
To activate a plugin:
Log in to your Elgg site with your administrator account
Go to Administration -> Configure -> Plugins
Find your plugin in the list of installed plugins and click on the enable button.
2.4 Performance
Make your site run as smoothly and responsively as possible.
Contents
2.4. Performance
31
32
Simplecache
By default, views are cached in the Elgg data directory for a given period of time. This removes the need for a view to
be regenerated on every page load.
This can be disabled by setting $CONFIG->simplecache_enabled = false; For best performance, make
sure this value is set to true.
This does lead to artifacts during development if you are editing themes in your plugin as the cached version will be
used in preference to the one provided by your plugin.
The simple cache can be disabled via the administration menu. It is recommended that you do this on your development
platform if you are writing Elgg plugins.
This cache is automatically flushed when a plugin is enabled, disabled or reordered, or when upgrade.php is executed.
For best performance, you can also create a symlink from /cache/ in your www root dir to the
/views_simplecache/ directory in the data directory you configured when you installed Elgg:
cd /path/to/wwwroot/
ln -s /path/to/dataroot/views_simplecache/ cache
If your webserver supports following symlinks, this will serve files straight off disk without booting up PHP each time.
System cache
The location of views are cached so that they do not have to be discovered (profiling indicated that page load took a
non-linear amount of time the more plugins were enabled due to view discovery). Elgg also caches information like
the language mapping and class map.
This can be disabled by setting $CONFIG->system_cache_enabled = false; For best performance, make
sure this value is set to true.
This is currently stored in files in your dataroot (although later versions of Elgg may use memcache). As with the
simple cache it is flushed when a plugin is enabled, disabled or reordered, or when upgrade.php is executed.
The system cache can be disabled via the administration menu, and it is recommended that you do this on your
development platform if you are writing Elgg plugins.
Database query cache
For the lifetime of a given pages execution, a cache of all SELECT queries is kept. This means that for a given page
load a given select query will only ever go out to the database once, even if it is executed multiple times. Any write
to the database will flush this cache, so it is advised that on complicated pages you postpone database writes until the
end of the page or use the execute_delayed_* functionality. This cache will be automatically cleared at the end
of a page load.
You may experience memory problems if you use the Elgg framework as a library in a PHP CLI script. This can be
disabled by setting $CONFIG->db_disable_query_cache = true;
Etags and Expires headers
These technologies tell your users browsers to cache static assets (CSS, JS, images) locally. Having these enabled
greatly reduces server load and improves user-perceived performance.
Use the Firefox yslow plugin or Chrome DevTools Audits to confirm which technologies are currently running on
your site.
2.4. Performance
33
Optionaly if you run multiple Elgg installations but use ony one Memcache server, you may want to add a namespace
prefix. In order to do this, uncomment the following line
$CONFIG->memcache_namespace_prefix = '';
Squid
We have had good results by using Squid to cache images for us.
Bytecode caching
There are numerous PHP code caches available on the market. These speed up your site by caching the compiled byte
code from your script meaning that your server doesnt have to compile the PHP code each time it is executed.
2.4.5 Hosting
Dont expect to run a site catering for millions of users on a cheap shared host. You will need to have your own host
hardware and access over the configuration, as well as lots of bandwidth and memory available.
34
2.5 Cron
Cron is a program available on Unix-based operating systems that enables users to run commands and scripts at set
intervals or at specific times.
Elggs cron handler allows administrators and plugin developers to setup jobs that need to be executed at set intervals.
Most common examples of cron jobs in Elgg include:
sending out queued notifications
rotating the system log in the database
collecting garbage in the database (compacting the database by removing entries that are no longer required)
Currently, Elgg supports the following hooks:
minute - Run every minute
fiveminute - Run every 5 minutes
2.5. Cron
35
2.5.2 Installation
The crontab needs to specify a script or command that will hit the Elgg cron pages. Two commonly available
programs for this are GET and wget. You will need to determine the location of one of these on your server. Your
crontab also needs to specify the location of your website.
#
#
#
#
#
#
#
#
Crontab example.
This file is an example of triggering Elgg cron events. It hits a URL to
trigger the events. For testing, you can simulate the cronjob by loading the
URL in a browser.
See http://learn.elgg.org/en/stable/admin/cron.html for more information
36
In the above example, change the ELGG and GET variables to match you server setup. If you have SSH access to
your Linux servers, type crontab -e and add your crontab configuration. If you already have a crontab configured,
you will have to merge Elgg information into it. If you dont have SSH access, you will have to use a web-based
configuration tool. This will vary depending on hosting provider.
If you choose the wget utility, you might want to consider these flags:
--output-document or -O to specify the location of the concatenated output file. For example, under
Debian: /usr/bin/wget --output-document=/dev/null. If you dont do that, a new file will be
created for each cron page load in the home directory of the cron user.
--spider to prevent the cron page from being downloaded.
On Windows servers, there is a number of cron emulators available.
For information on setting up cron jobs using cPanel see cPanel Docs.
In the command field, enter the appropriate link of the cron page. For example, for a weekly cron job, enter the
command as http://www.example.com/cron/weekly/.
To see if your cron jobs are running, visit Statistics > Cron in your Elgg admin panel.
37
2.6.1 Introduction
Why
Shared hosting providers typically dont provide an automated way to backup your Elgg installation. This article will
address a method of accomplishing this task.
In IT there are often many ways to accomplish the same thing. Keep that in mind. This article will explain one method
to backup and restore your Elgg installation on a shared hosting provider that uses the CPanel application. However,
the ideas presented here can be tailored to other applications as well. The following are typical situations that might
require a procedure such as this:
Disaster Recovery
Moving your Elgg site to a new host
Duplicating an installation
What
Topics covered:
Full backups of the Elgg directories and MySQL databases are performed daily (automated)
The backups are sent to an off-site location via FTP (automated)
The local backups are deleted after successful transfer to the off-site location (automatic)
Five days of backups will be maintained (automated)
Restoration of data to the new host (manual)
This process was composed with assistance from previous articles in the Elgg documentation wiki.
Assumptions
The following assumptions have been made:
The Elgg program directory is /home/userx/public_html
The Elgg data directory is /home/userx/elggdata
Youve created a local directory for your backups at /home/userx/sitebackups
You have an off-site FTP server to send the backup files to
The directory that you will be saving the off-site backups to is /home/usery/sitebackups/
You will be restoring the site to a second shared hosting provider in the /home/usery/public_html
directory
Important: Be sure to replace userx, usery, http://mynewdomain.com and all passwords with values that
reflect your actual installation!
Just copy the script to a text file and name the file with a .pl extension. You can use any text editor to update the file.
Change the following to reflect your directory structure:
# ENTER THE PATH TO THE DIRECTORY YOU WANT TO BACKUP, NO TRAILING SLASH
$directory_to_backup = '/home/userx/public_html';
$directory_to_backup2 = '/home/userx/elggdata';
# ENTER THE PATH TO THE DIRECTORY YOU WISH TO SAVE THE BACKUP FILE TO, NO TRAILING SLASH
$backup_dest_dir = '/home/userx/sitebackups';
Save the file with the .pl extension (for the purposes of this article we will name the file:
elgg-ftp-backup-script.pl) and upload it to the following directory /home/userx/sitebackups
Be aware that you can turn off FTP and flip a bit in the script so that it does not delete the local backup file in the event
that you dont want to use off-site storage for your backups.
Configure the backup Cron job
Login to your CPanel application and click on the Cron Jobs link.
In the Common Settings
dropdown choose Once a day and type the following in the command field /usr/bin/perl
/home/userx/sitebackups/elgg-ftp-backup-script.pl
Click on the Add New Cron Job button. Daily full backups are now scheduled and will be transferred off-site.
Configure the cleanup Cron job
If you are sending your backups, via FTP, to another shared hosting provider that uses the CPanel application or youve
turned off FTP altogether you can configure your data retention as follows.
Login to your CPanel application for your FTP site, or locally if youre not using FTP, and click on the Cron Jobs
link. In the Common Settings dropdown choose Once a day and type the following in the command field find
/home/usery/sitebackups/full_* -mtime +4 -exec rm {} \;
The -mtime X parameter will set the number of days to retain backups. All files older than x number of days will
be deleted. Click on the Add New Cron Job button. You have now configured your backup retention time.
39
40
41
Upload the settings.php file back to the new host - overwriting the existing file.
Open the phpMyAdmin tool on the new host from the CPanel. Select the usery_elgg database on the left and click
the SQL tab on the top of the page. Run the following SQL queries against the usery_elgg database:
Change the installation path
UPDATE `elgg_datalists` SET `value` = "/home/usery/public_html/grid/" WHERE `name` = "path";
UPDATE elgg_metastrings set string = '/home/usery/elggdata/' WHERE id = (SELECT value_id from elgg_me
2.6.4 Congratulations!
If you followed the steps outlined here you should now have a fully functional copy of your primary Elgg installation.
42
2.6.5 Related
FTP backup script
Here is an automated script for backing up an Elgg installation.
#!/usr/bin/perl -w
# FTP Backup
use Net::FTP;
# DELETE BACKUP AFTER FTP UPLOAD (0 = no, 1 = yes)
$delete_backup = 1;
# ENTER THE PATH TO THE DIRECTORY YOU WANT TO BACKUP, NO TRAILING SLASH
$directory_to_backup = '/home/userx/public_html';
$directory_to_backup2 = '/home/userx/elggdata';
# ENTER THE PATH TO THE DIRECTORY YOU WISH TO SAVE THE BACKUP FILE TO, NO TRAILING SLASH
$backup_dest_dir = '/home/userx/sitebackups';
# BACKUP FILE NAME OPTIONS
($a,$d,$d,$day,$month,$yearoffset,$r,$u,$o) = localtime();
$year = 1900 + $yearoffset;
$site_backup_file = "$backup_dest_dir/site_backup-$day-$month-$year.tar.gz";
$full_backup_file = "$backup_dest_dir/full_site_backup-$day-$month-$year.tar.gz";
# MYSQL BACKUP PARAMETERS
$dbhost = 'localhost';
$dbuser = 'userx_elgg';
$dbpwd = 'dbpassword';
$mysql_backup_file_elgg = "$backup_dest_dir/mysql_elgg-$day-$month-$year.sql.gz";
# ENTER DATABASE NAME
$database_names_elgg = 'userx_elgg';
# FTP PARAMETERS
$ftp_backup = 1;
$ftp_host = "FTP HOSTNAME/IP";
$ftp_user = "ftpuser";
$ftp_pwd = "ftppassword";
$ftp_dir = "/";
# SYSTEM COMMANDS
$cmd_mysqldump = '/usr/bin/mysqldump';
$cmd_gzip = '/usr/bin/gzip';
# CURRENT DATE / TIME
($a,$d,$d,$day,$month,$yearoffset,$r,$u,$o) = localtime();
$year = 1900 + $yearoffset;
# BACKUP FILES
$syscmd = "tar --exclude $backup_dest_dir" . "/* -czf $site_backup_file $directory_to_backup $directo
43
system($syscmd);
# CREATING FULL SITE BACKUP FILE
$syscmd = "tar -czf $full_backup_file $mysql_backup_file_elgg $site_backup_file";
system($syscmd);
# DELETING SITE AND MYSQL BACKUP FILES
unlink($mysql_backup_file_elgg);
unlink($site_backup_file);
# UPLOADING FULL SITE BACKUP TO REMOTE FTP SERVER
if($ftp_backup == 1)
{
my $ftp = Net::FTP->new($ftp_host, Debug => 0)
or die "Cannot connect to server: $@";
$ftp->login($ftp_user, $ftp_pwd)
or die "Cannot login ", $ftp->message;
$ftp->cwd($ftp_dir)
or die "Can't CWD to remote FTP directory ", $ftp->message;
$ftp->binary();
$ftp->put($full_backup_file)
or warn "Upload failed ", $ftp->message;
$ftp->quit();
}
# DELETING FULL SITE BACKUP
if($delete_backup = 1)
{
unlink($full_backup_file);
}
Duplicate Installation
44
Contents
Introduction
Why Duplicate an Elgg Installation?
What Is Not Covered in This Tutorial
Before You Start
Copy Elgg Code to the Test Server
Copy Data to the Test Server
Edit engine/settings.php
Copy Elgg Database
Database Entries
Change the installation path
Change the data directory
Change the site URL
Change the filestore data directory
Check .htaccess
Update Webserver Config
Run upgrade.php
Tips
Related
Introduction
Why Duplicate an Elgg Installation? There are many reasons you may want to duplicate an Elgg installation:
moving the site to another server, creating a test or development server, and creating functional backups are the most
common. To create a successful duplicate of an Elgg site, 3 things need to be copied:
Database
Data from the data directory
Code
Also at least 5 pieces of information must be changed from the copied installation:
engine/settings.php file
.htaccess file (Apache) or Nginx configuration depending on server used
database entry for your site entity
database entry for the installation path
database entry for the data path
What Is Not Covered in This Tutorial This tutorial expects a basic knowledge of Apache, MySQL, and Linux
commands. As such, a few things will not be covered in this tutorial. These include:
How to backup and restore MySQL databases
How to configure Apache to work with Elgg
How to transfer files to and from your production server
Before You Start Before you start, make sure the Elgg installation you want to duplicate is fully functional. You
will also need the following items:
A backup of the live Elgg database
2.6. Backup and Restore
45
cp -a /var/www/elgg/ /var/www/elgg_test/
If you dont have shell access to your server and have to ftp the data, you may need to change ownership and permissions on the files.
Note: You also need to delete the views cache on the test server after the copy process. This is a directory called
views_simplecache in your data directory and the directory called system_cache .
46
Edit engine/settings.php
The engine/settings.php file contains the database configuration details. These need to be adjusted for your
new test Elgg installation. In our example, well look in /var/www/elgg_test/engine/settings.php and
find the lines that look like this:
// Database username
$CONFIG->dbuser = 'db_user';
// Database password
$CONFIG->dbpass = 'db_password';
// Database name
$CONFIG->dbname = 'elgg_production';
// Database server
// (For most configurations, you can leave this as 'localhost')
$CONFIG->dbhost = 'localhost';
// Database table prefix
// If you're sharing a database with other applications, you will want to use this
// to differentiate Elgg's tables.
$CONFIG->dbprefix = 'elgg';
Note: Notice the $CONFIG->dbname has changed to reflect our new database.
Now the database must be copied from elgg_production to elgg_test. See your favorite MySQL managers
documentation for how to make a duplicate database. You will generally export the current database tables to a file,
create the new database, and then import the tables that you previously exported.
You have two options on updating the values in the database. You could change the values in the export file or you
could import the file and change the values with database queries. One advantage of modifying the dump file is that
2.6. Backup and Restore
47
you can also change links that people have created to content within your site. For example, if people have bookmarked
pages using the bookmark plugin, the bookmarks will point to the old site unless your update their URLs.
Database Entries
We must now change 4 entries in the database. This is easily accomplished with 4 simple SQL commands:
Change the installation path
UPDATE `elgg_datalists` SET `value` = "/var/www/elgg_test/" WHERE `name` = "path";
Check .htaccess
If you have made changes to .htaccess that modify any paths, make sure you update them in the test installation.
Update Webserver Config
For this example, you must edit the Apache config to enable a subdomain with a document root of
/var/www/elgg_test/. If you plan to install into a subdirectory of your document root, this step is unnecessary.
If youre using Nginx, you need
install/config/nginx.dist.
48
to
update
server
config
to
match
new
paths
based
on
Run upgrade.php
It is a good idea to keep a test server around to experiment with installing new mods and doing development work. If
you automate restorations to the elgg_test database, changing the $CONFIG values and adding the follow lines to
the end of the elgg_test/engine/settings.php file will allow seamless re-writing of the MySQL database
entries.
$con = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass);
mysql_select_db($CONFIG->dbname, $con);
$sql = "UPDATE {$CONFIG->dbprefix}datalists
SET value = '/var/www/test_elgg/'
WHERE name = 'path'";
mysql_query($sql);
print mysql_error();
$sql = "UPDATE {$CONFIG->dbprefix}datalists
SET value = '/var/data/test_elgg/'
WHERE name = 'dataroot'";
mysql_query($sql);
print mysql_error();
$sql = "UPDATE {$CONFIG->dbprefix}sites_entity
SET url = 'http://test.myelgg.org/'";
mysql_query($sql);
$sql = "UPDATE {$CONFIG->dbprefix}metastrings
SET string = '/var/data/elgg_test/'
WHERE id = (
SELECT value_id
FROM {$CONFIG->dbprefix}metadata
WHERE name_id = (
SELECT *
FROM (
SELECT id
FROM {$CONFIG->dbprefix}metastrings
WHERE string = 'filestore::dir_root'
) as ms2
)
LIMIT 1
)";
mysql_query($sql);
print mysql_error();
Related
See also:
Backup and Restore
49
2.7.1 Introduction
Why Duplicate an Elgg Installation?
There are many reasons you may want to duplicate an Elgg installation: moving the site to another server, creating a
test or development server, and creating functional backups are the most common. To create a successful duplicate of
an Elgg site, 3 things need to be copied:
Database
Data from the data directory
Code
Also at least 5 pieces of information must be changed from the copied installation:
engine/settings.php file
.htaccess file (Apache) or Nginx configuration depending on server used
database entry for your site entity
database entry for the installation path
database entry for the data path
What Is Not Covered in This Tutorial
This tutorial expects a basic knowledge of Apache, MySQL, and Linux commands. As such, a few things will not be
covered in this tutorial. These include:
How to backup and restore MySQL databases
50
cp -a /var/www/elgg/ /var/www/elgg_test/
51
If you dont have shell access to your server and have to ftp the data, you may need to change ownership and permissions on the files.
Note: You also need to delete the views cache on the test server after the copy process. This is a directory called
views_simplecache in your data directory and the directory called system_cache .
52
Note: Notice the $CONFIG->dbname has changed to reflect our new database.
53
to
update
server
config
to
match
new
paths
based
on
2.7.10 Tips
It is a good idea to keep a test server around to experiment with installing new mods and doing development work. If
you automate restorations to the elgg_test database, changing the $CONFIG values and adding the follow lines to
the end of the elgg_test/engine/settings.php file will allow seamless re-writing of the MySQL database
entries.
$con = mysql_connect($CONFIG->dbhost, $CONFIG->dbuser, $CONFIG->dbpass);
mysql_select_db($CONFIG->dbname, $con);
$sql = "UPDATE {$CONFIG->dbprefix}datalists
SET value = '/var/www/test_elgg/'
WHERE name = 'path'";
mysql_query($sql);
print mysql_error();
$sql = "UPDATE {$CONFIG->dbprefix}datalists
SET value = '/var/data/test_elgg/'
WHERE name = 'dataroot'";
mysql_query($sql);
print mysql_error();
$sql = "UPDATE {$CONFIG->dbprefix}sites_entity
SET url = 'http://test.myelgg.org/'";
mysql_query($sql);
$sql = "UPDATE {$CONFIG->dbprefix}metastrings
SET string = '/var/data/elgg_test/'
WHERE id = (
SELECT value_id
FROM {$CONFIG->dbprefix}metadata
WHERE name_id = (
SELECT *
FROM (
SELECT id
FROM {$CONFIG->dbprefix}metastrings
WHERE string = 'filestore::dir_root'
54
) as ms2
)
LIMIT 1
)";
mysql_query($sql);
print mysql_error();
2.7.11 Related
See also:
Backup and Restore
55
2.8.2 Guidelines
In addition to the site-wide Terms and Policies, following these guidelines keeps our community site useful and safe
for everyone.
Content
All content must be safe for work: PG in the US and UK. If your Elgg site has adult content and you have been asked
to post a link, please mark it NSFW (Not Safe For Work) so people know.
Excessive swearing in any language will not be tolerated.
Mood
Working with technical problems can be frustrating. Please keep the community site free of frustration. If youre feeling anxious, take a step away and do something else. Threatening or attacking community members, core developers,
or plugin developers will not help solve your problem and will likely get you banned.
56
Advertising
Advertising is not allowed. Posts with any sort of advertising will be moderated.
Asking for money / Offering to pay
Dont ask for money on the community site. Likewise, dont offer to pay for answers. If you are looking for custom
development, post to the Professional Services group. Posts asking for money or recommending a commercial plugin
may be moderated.
Links
If youre having a problem with a live site, please provide a link to it.
That said, the community site is not a back linking service or SEO tool. Excessive linking will be moderated and your
account may be banned.
Signatures
Theres a reason Elgg doesnt have an option for signatures: they cause clutter and distract from the conversation.
Users are discouraged from using signatures on the community site, and signatures with links or advertising will be
removed.
Bumping, +1, me too
Dont do it. If your question hasnt been answered, see the top of this document for tips. These types of post add
nothing to the conversation and may be moderated.
Posting Code
Long bits of code are confusing to read through in a forums context. Please use http://elgg.pastebin.com to post long
bits of code and provide the Paste Bin link instead of directly posting the code.
57
58
CHAPTER 3
Developer Guides
59
3.1.4 Summary
Resist the temptation Editing existing files is quick and easy, but doing so heavily risks the maintainability,
security, and stability of your site.
When receiving advice, consider if the person telling you to modify core will be around to rescue you if you run
into trouble later!
Apply these principle to software in general. If you can avoid it, dont modify third party plugins either, for
the same reasons: Plugin authors release new versions, too, and you will want those updates.
3.2 Plugins
Plugins must provide a start.php and manifest.xml file in the plugin root in order to be recognized by Elgg.
3.2.1 start.php
The start.php file bootstraps plugin by registering event listeners and plugin hooks.
3.2.3 manifest.xml
Elgg plugins are required to have a manifest.xml file in the root of a plugin.
The manifest.xml file includes information about the plugin itself, requirements to run the plugin, and optional
information including where to display the plugin in the admin area and what APIs the plugin provides.
Syntax
The manifest file is a standard XML file in UTF-8. Everything is a child of the <plugin_manifest> element.
<?xml version="1.0" encoding="UTF-8" ?>
<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
60
<parent_name>
<child_name>value</child_name>
<child_name_2>value_2</child_name_2>
</parent_name>
Required Elements
All plugins are required to define the following elements in their manifest files:
id - This has the name as the directory that the plugin uses.
name - The display name of the plugin.
author - The name of the author who wrote the plugin.
version - The version of the plugin.
description - A description of the what the plugin provides, its features, and other relevant information
requires - Each plugin must specify the release of Elgg it was developed for. See the plugin Dependencies page
for more information.
Available Elements
In addition to the require elements above, the follow elements are available to use:
blurb - A short description of the plugin.
category - The category of the plugin. It is recommended to follow the [[Plugin_Guidelines|plugin guidelines]]
and use one of the defined categories. There can be multiple entries.
conflicts - Specifies that the plugin conflicts with a certain system configuration.
copyright - The plugins copyright information.
license - The plugins license information.
provides - Specifies that this plugin provides the same functionality as another Elgg plugin or a PHP extension.
screenshot - Screenshots of the plugin. There can be multiple entries. See the advanced example for syntax.
suggests - Parallels the requires system, but doesnt affect if the plugin can be enabled. Used to suggest other
plugins that interact or build on the plugin.
website - A link to the website for the plugin.
See also:
Plugin Dependencies
Simple Example
This manifest file is the bare minimum a plugin must have.
3.2. Plugins
61
<requires>
<type>elgg_release</type>
<version>1.9</version>
</requires>
</plugin_manifest>
Advanced example
This example uses all of the available elements:
3.2.4 Related
Plugin skeleton
The following is the standard for plugin structure in Elgg as of Elgg 2.0.
62
Example Structure
The following is an example of a plugin with standard structure. For further explanation of this structure, see the
details in the following sections. Your plugin may not need all the files listed
The following files for plugin example would go in /mod/example/
actions/
example/
action.php
other_action.php
classes/
VendorNamespace/
ExampleClass.php
languages/
en.php
vendors/
example_3rd_party_lib/
views/
default/
example/
component.css
component.js
component.png
forms/
example/
action.php
other_action.php
object/
example.php
example/
context1.php
context2.php
plugins/
example/
settings.php
usersettings.php
resources/
example/
all.css
all.js
all.php
owner.css
owner.js
owner.php
widgets/
example_widget/
content.php
edit.php
activate.php
deactivate.php
CHANGES.txt
COPYRIGHT.txt
INSTALL.txt
LICENSE.txt
manifest.xml
README.txt
start.php
3.2. Plugins
63
Required Files
Plugins must provide a start.php and manifest.xml file in the plugin root in order to be recognized by Elgg.
Therefore the following is the minimally compliant structure:
mod/example/
start.php
manifest.xml
Actions
Plugins should place scripts for actions an actions/ directory, and furthermore should use the name of the action
to determine the location within that directory.
For example, the action my/example/action would go in my_plugin/actions/my/example/action.php.
This makes it very obvious which script is associated with which action.
Similarly,
the body of the form that submits to this action should be located in
forms/my/example/action.php. Not only does this make the connection b/w action handler, form
code, and action name obvious, but it allows you to use the new (as of Elgg 1.8) elgg_view_form() function
easily.
Text Files
Plugins may provide various *.txt as additional documentation for the plugin. These files must be in Markdown syntax
and will generate links on the plugin management sections.
README.txt should provide additional information about the plugin of an unspecified nature
COPYRIGHT.txt If included, must provide an explanation of the plugins copyright, besides what is included in
manifest.xml
LICENSE.txt If included, must provide the text of the license that the plugin is released under.
INSTALL.txt If included, must provide additional instructions for installing the plugin if the process is sufficiently
complicated (e.g. if it requires installing third party libraries on the host machine, or requires acquiring an API
key from a third party).
CHANGES.txt If included, must provide a list of changes for their plugin, grouped by version number, with the most
recent version at the top.
Plugins may include additional *.txt files besides these, but no interface is given for reading them.
Pages
To render full pages, plugins should use resource views (which have names beginning with resources/). This
allows other plugins to easily replace functionality via the view system.
Note: The reason we encourage this structure is
To form a logical relationship between urls and scripts, so that people examining the code can have an idea of
what it does just by examining the structure.
To clean up the root plugin directory, which historically has quickly gotten cluttered with the page handling
scripts.
64
Classes
Elgg provides PSR-0 autoloading out of every active plugins classes/ directory.
Youre encouraged to follow the PHP-FIG standards when writing your classes.
Note: Files with a .class.php extension will not be recognized by Elgg.
Vendors
Included third-party libraries of any kind should be included in the vendors/ folder in the plugin root. Though this
folder has no special significance to the Elgg engine, this has historically been the location where Elgg core stores its
third-party libraries, so we encourage the same format for the sake of consistency and familiarity.
Views
In order to override core views, a plugins views must be placed in a views/. This directory has special meaning to
Elgg as views defined here automatically override Elgg cores version of those views. For more info, see Views.
Javascript and CSS will live in the views system. See JavaScript.
activate.php and deactivate.php
The activate.php and deactivate.php files contain procedural code that will run respectively upon plugin
activation or deactivation. Use these files to perform one-time events such as registering a persistent admin notice,
registering subtypes, or performing garbage collection when deactivated.
Plugin Dependencies
In Elgg 1.8 a plugin dependencies system was introduced to prevent plugins from being used on incompatible systems.
3.2. Plugins
65
Contents
Overview
Verbs
Requires
Mandatory requires: elgg_release
Suggests
Conflicts
Provides
Types
elgg_release
plugin
priority
php_extension
php_ini
php_version
Comparison Operators
Quick Examples
Requires Elgg 1.8.2 or higher
Requires the Groups plugin is active
Requires to be after the Profile plugin if Profile is active
Conflicts with The Wire plugin
Requires at least 256 MB memory in PHP
Requires at least PHP version 5.3
Suggest the TidyPics plugin is loaded
Overview
The dependencies system is controlled through a plugins manifest.xml file. Plugin authors can specify that a
plugin:
Requires certain Elgg versions, Elgg plugins, PHP extensions, and PHP settings.
Suggests certain Elgg versions, Elgg plugins, PHP extensions, and PHP settings.
Conflicts with certain Elgg versions or Elgg plugins.
Provides the equivalent of another Elgg plugin or PHP extension.
The dependency system uses the four verbs above (requires, suggests, conflicts, and provides) as parent
elements to indicate what type of dependency is described by its children. All dependencies have a similar format with
similar options:
<verb>
<type>type</type>
<noun>value</noun>
<noun2>value2</noun2>
</verb>
66
Verbs
With the exception of provides, all verbs use the same six types with differing effects, and the type options are the
same among the verbs. provides only supports plugin and php_extension.
Requires Using a requires dependency means that the plugin cannot be enabled unless the dependency is exactly
met.
Mandatory requires: elgg_release Every plugin must have at least one requires: the version of Elgg the plugin is
developed for. This is specified by the Elgg API release (1.8). The default comparison >=, but you can specify
your own by passing the <comparison> element.
Using elgg_release:
<requires>
<type>elgg_release</type>
<version>1.8</version>
</requires>
Suggests suggests dependencies signify that the plugin author suggests a specific system configuration, but it is
not required to use the plugin. The suggestions can also be another plugin itself which could interact, extend, or be
extended by this plugin, but is not required for it to function.
Suggest another plugin:
<suggests>
<type>plugin</type>
<name>twitter_api</name>
<version>1.0</version>
</suggests>
Conflicts conflicts dependencies mean the plugin cannot be used under a specific system configuration.
Conflict with any version of the profile plugin:
<conflicts>
<type>plugin</type>
<name>profile</name>
</conflicts>
3.2. Plugins
67
Provides provides dependencies tell Elgg that this plugin is providing the functionality of another plugin or PHP
extension. Unlike the other verbs, it only supports two types: plugin and php_extension.
The purpose of this is to provide interchangeable APIs implemented by different plugins. For example, the twitter_services plugin provides an API for other plugins to Tweet on behalf of the user via curl and Oauth. A plugin
author could write a compatible plugin for servers without curl support that uses sockets streams and specify that it
provides twitter_services. Any plugins that suggest or require twitter_services would then know they can work.
<provides>
<type>plugin</type>
<name>twitter_services</name>
<version>1.8</version>
</provides>
Note: All plugins provide themselves as their plugin id (directory name) at the version defined in the their manifest.
Types
Every dependency verb has a mandatory <type> element that must be one of the following six values:
1. elgg_release - The release version of Elgg (1.8)
2. plugin - An Elgg plugin
3. priority - A plugin load priority
4. php_extension - A PHP extension
5. php_ini - A PHP setting
6. php_version - A PHP version
Note: provides only supports plugin and php_extension types.
Every type is defined with a dependency verb as the parent element. Additional option elements are at the same level
as the type element:
<verb>
<type>type</type>
<option_1>value_1</option_1>
<option_2>value_2</option_2>
</verb>
elgg_release These concern the API and release versions of Elgg and requires the following option element:
version - The API or release version
The following option element is supported, but not required:
comparison - The comparison operator to use. Defaults to >= if not passed
plugin Specifies an Elgg plugin by its ID (directory name). This requires the following option element:
name - The ID of the plugin
The following option elements are supported, but not required:
version - The version of the plugin
68
php_ini This checks PHP settings. The following option elements are required:
name - The name of the setting to check
value - The value of the setting to compare against
The following options are supported, but not required:
comparison - The comparison operator to use. Defaults to ==
php_version This checks the PHP version. The following option elements are required:
version - The PHP version
The following option element is supported, but not required:
comparison - The comparison operator to use. Defaults to >= if not passed
Comparison Operators
Dependencies that check versions support passing a custom operator via the <comparison> element.
The follow are valid comparison operators:
< or lt
<= or le
=, ==, or eq
!=, <>, or ne
> or gt
>= or ge
If <comparison> is not passed, the follow are used as defaults, depending upon the dependency type:
requires->elgg_release: >=
3.2. Plugins
69
requires->plugin: >=
requires->php_extension: =
requires->php_ini: =
all conflicts: =
Note: You must escape < and > to > and <. For comparisons that use these values, it is recommended you
use the string equivalents instead!
Quick Examples
70
Contents
Purpose
All
User
User friends
Single entity
Add
Edit
Group list
URL
page_handler/all
page_handler/owner/<username>
page_handler/friends/<username>
page_handler/view/<guid>/<title>
page_handler/add/<container_guid>
page_handler/edit/<guid>
page_handler/group/<guid>/owner
71
Purpose
Add
Edit
Delete
mod/blog/actions/blog/save.php =>
URL
action/plugin/save
action/plugin/save
action/plugin/delete
Make the delete action accept action/<handler>/delete?guid=<guid> so the metadata entity menu
has the correct URL by default
If updating a 1.7 plugin, replace calls to functions deprecated in 1.7 because these will produce visible errors on
every load in 1.8
3.3.4 Actions
Actions are transient states to perform an action such as updating the database or sending a notification to a user. Used
correctly, actions provide a level of access control and prevent against CSRF attacks.
Actions require action (CSRF) tokens to be submitted via GET/POST, but these are added automatically by
elgg_view_form() and by using the is_action argument of the output/url view.
Action best practices
Action files are included within Elggs action system; like views, they are not regular scripts executable by users. Do
not boot the Elgg core in your file and direct users to load it directly.
72
Because actions are time-sensitive they are not suitable for links in emails or other delayed notifications. An example
of this would be invitations to join a group. The clean way to create an invitation link is to create a page handler for
invitations and email that link to the user. It is then the page handlers responsibility to create the action links for a
user to join or ignore the invitation request.
Consider that actions may be submitted via XHR requests, not just links or form submissions.
3.3.6 Recommended
These points are good ideas, but are not yet in the official guidelines. Following these suggestions will help to keep
your plugin consistent with Elgg core.
Update the widget views (see the blog or file widgets)
Update the group profile widget using blog or file plugins as example
Update the forms
Move form bodies to /forms/<handler>/<action> to use Evans new elgg_view_form()
Use input views in form bodies rather than html
Add a function that prepares the form (see mod/file/lib/file.php for example)
Integrate sticky forms (see the file plugins upload action and form prepare function)
Clean up CSS/HTML
Should be able to remove almost all CSS (look for patterns that can be moved into core if you need
CSS)
Use hyphens rather than underscores in classes/ids
Update the manifest.xml file to the 1.8 format. Use http://el.gg/manifest17to18 to automate this
Do not use the bundled category with your plugins. That is for plugins distributed with Elgg
Update functions deprecated in 1.8.
Many registration functions simply added an elgg_ prefix for consistency
See /engine/lib/deprecated-1.8.php for the full list. You can also set the debug level to
warning to get visual reminders of deprecated functions
3.4 Accessibility
This page aims to list and document accessibility rules and best practices, to help core and plugins developpers to
make Elgg the most accessible social engine framework that everyone dreams of.
Note: This is an ongoing work, please contribute on Github if you have some skills in this field!
3.4. Accessibility
73
74
3.5 Ajax
3.5.1 Actions
From JavaScript we can execute actions via XHR POST operations. Heres an example action and script for some
basic math:
// in myplugin/actions/do_math.php
if (!elgg_is_xhr()) {
register_error('Sorry, Ajax only!');
forward();
}
$arg1 = (int)get_input('arg1');
$arg2 = (int)get_input('arg2');
system_message('We did it!');
echo json_encode([
'sum' => $arg1 + $arg2,
'product' => $arg1 * $arg2,
]);
elgg.action('do_math', {
data: {
arg1: 1,
arg2: 2
},
success: function (wrapper) {
if (wrapper.output) {
alert(wrapper.output.sum);
alert(wrapper.output.product);
} else {
// the system prevented the action from running
}
}
});
75
with the
To make sure Ajax actions can only be executed via XHR, check elgg_is_xhr() first.
The action JSON response wrapper
{
current_url: {String} "http://example.org/action/example/math", // not very useful
forward_url: {String} "http://example.org/foo", ...if forward('foo') was called
output: {String|Object} from echo in action
status: {Number} 0 = success. -1 = an error was registered.
system_messages: {Object}
}
Warning: Its probably best to rely only on the output key, and validate it in case the PHP action could not run
for some reason, e.g. the user was logged out or a CSRF attack did not provide tokens.
76
},
success: function (output) {
$('.myplugin-link').html(output);
}
});
The Ajax view system works significantly differently than the action system.
There are no access controls based on session status.
Non-XHR requests are automatically rejected.
GET vars are injected into $vars in the view.
If the request contains $_GET[guid], the system sets $vars[entity] to the corresponding entity or
false if it cant be loaded.
Theres no wrapper object placed around the view output.
System messages/errors shouldnt be used, as they dont display until the user loads another page.
Depending on the views suffix (.js, .html, .css, etc.), a corresponding Content-Type header is added.
Warning: Unlike views rendered server-side, Ajax views must treat $vars as completely untrusted user data.
* The GET params will be passed as ``$vars`` to *your* view, **not** the ``input/form`` view.
* If you need to set ``$vars`` in the ``input/form`` view, you'll need to use the ``("view_vars", "in
plugin hook (this can't be done client-side).
3.5. Ajax
77
Warning: Unlike views rendered server-side, Ajax views must treat $vars as completely untrusted user data.
Review the use of $vars in an existing form before registering it for Ajax fetching.
elgg.post() is a wrapper for jQuerys $.ajax(), but forces POST and does URL normalization.
3.6 Authentication
Elgg provides everything needed to authenticate users via username/email and password out of the box, including:
remember-me cookies for persistent login
password reset logic
secure storage of passwords
logout
UIs for accomplishing all of the above
All thats left for you to do as a developer is to use the built-in authentication functions to secure your pages and
actions.
The returned object is an ElggUser so you can use all the methods and properties of that class to access information
about the user. If the user is not logged in, this will return null, so be sure to check for that first.
78
3.6.2 Gatekeepers
Gatekeeper functions allow you to manage how code gets executed by applying access control rules.
Forward a user to the front page if they are not logged in with elgg_gatekeeper():
elgg_gatekeeper();
echo "Information for logged-in users only";
Note: In Elgg 1.8 and below this function was called gatekeeper()
Forward a user to the front page unless they are an admin with elgg_admin_gatekeeper():
elgg_admin_gatekeeper();
echo "Information for admins only";
Note: In Elgg 1.8 and below this function was called admin_gatekeeper()
Prevent CSRF attacks with action_gatekeeper().
action_gatekeeper();
// Mutate some state in the database on behalf of the logged in user...
3.6. Authentication
79
3.6.4 Importance
By default an authentication module is registered with an importance of sufficient.
In a list of authentication modules; if any one marked sufficient returns true, pam_authenticate() will also
return true. The exception to this is when an authentication module is registered with an importance of required. All
required modules must return true for pam_authenticate() to return true, regardless of whether all sufficient
modules return true.
3.7 Context
Within the Elgg framework, context can be used to by your plugins functions to determine if they should run or not.
You will be registering callbacks to be executed when particular events are triggered. Sometimes the events are generic
and you only want to run your callback when your plugin caused the event to be triggered. In that case, you can use
the pages context.
You can explicitly set the context with set_context(). The context is a string and typically you set it to the
name of your plugin. You can retrieve the context with the function get_context(). Its however better to use
elgg_push_context($string) to add a context to the stack. You can check if the context you want in in the
current stack by calling elgg_in_context($context). Dont forget to pop (with elgg_pop_context())
the context after you push one and dont need it anymore.
If you dont set it, Elgg tries to guess the context. If the page was called through the page handler, the context is set to
the name of the handler which was set in elgg_register_page_handler(). If the page wasnt called through
the page handler, it uses the name of your plugin directory. If it cannot determine that, it returns main as the default
context.
Sometimes a view will return different HTML depending on the context. A plugin can take advantage of that by setting
the context before calling elgg_view() on the view and then setting the context back. This is frequently done with
the search context.
3.8 Database
Persist user-generated content and settings with Elggs generic storage API.
80
Contents
Entities
Creating an object
Loading an object
Displaying entities
Adding, reading and deleting annotations
Extending ElggEntity
Advanced features
Pre-1.8 Notes
Custom database functionality
Example: Run SQL script on plugin activation
Systemlog
System log storage
Creating your own system log
3.8.1 Entities
Creating an object
To create an object in your code, you need to instantiate an ElggObject. Setting data is simply a matter of adding
instance variables or properties. The built-in properties are:
guid The entitys GUID; set automatically
owner_guid The owning users GUID
site_guid The owning sites GUID. This is set automatically when an instance of ElggObject gets created)
subtype A single-word arbitrary string that defines what kind of object it is, for example blog
access_id An integer representing the access level of the object
title The title of the object
description The description of the object
The object subtype is a special property. This is an arbitrary string that describes what the object is. For example,
if you were writing a blog plugin, your subtype string might be blog. Its a good idea to make this unique, so that
other plugins dont accidentally try and use the same subtype. For the purposes of this document, lets assume were
building a simple forum. Therefore, the subtype will be forum:
$object = new ElggObject();
$object->subtype = "forum";
$object->access_id = 2;
$object->save();
access_id is another important property. If you dont set this, your object will be private, and only the creator user
will be able to see it. Elgg defines constants for the special values of access_id:
ACCESS_PRIVATE Only the owner can see it
ACCESS_FRIENDS Only the owner and his/her friends can see it
ACCESS_LOGGED_IN Any logged in user can see it
ACCESS_PUBLIC Even visitors not logged in can see it
3.8. Database
81
Saving the object will automatically populate the $object->guid property if successful. If you change any more
base properties, you can call $object->save() again, and it will update the database for you.
You can set metadata on an object just like a standard property. Lets say we want to set the SKU of a product:
$object->SKU = 62784;
If you assign an array, all the values will be set for that metadata. This is how, for example, you set tags.
Metadata cannot be persisted to the database until the entity has been saved, but for convenience, ElggEntity can cache
it internally and save it when saving the entity.
Loading an object
By GUID
$entity = get_entity($guid);
if (!$entity) {
// The entity does not exist or you're not allowed to access it.
}
But what if you dont know the GUID? There are several options.
By user, subtype or site
If you know the user ID you want to get objects for, or the subtype, or the site, you have several options. The easiest
is probably to call the procedural function elgg_get_entities:
$entities = elgg_get_entities(array(
'type' => $entity_type,
'subtype' => $subtype,
'owner_guid' => $owner_guid,
));
This will return an array of ElggEntity objects that you can iterate through. elgg_get_entities paginates
by default, with a limit of 10; and offset 0.
You can leave out owner_guid to get all objects and leave out subtype or type to get objects of all types/subtypes.
If you already have an ElggUser e.g. elgg_get_logged_in_user_entity, which always has the current
users object when youre logged in you can simply use:
$objects = $user->getObjects($subtype, $limit, $offset)
Note: As of Elgg 1.10 the default behaviour of elgg_get_entities_from_annotations was brought inline with the rest
of the elgg_get_entities* functions.
Pre Elgg 1.10 the sorting of the entities was based on the latest addition of an annotation (in $options your could add
$options[order_by] = maxtime ASC or $options[order_by] = maxtime DESC. As of Elgg 1.10 this was changed
to the creation time of the entity, just like the rest of the elgg_get_entities* functions. To get the old behaviour back
add the following to your $options:
$options['selects'] = array('MAX(n_table.time_created) AS maxtime');
$options['group_by'] = 'n_table.entity_guid';
$options['order_by'] = 'maxtime ASC'
or
$options['order_by'] = 'maxtime DESC'
Displaying entities
In order for entities to be displayed in listing functions you need to provide a view for the entity in the views system.
To display an entity, create a view EntityType/subtype where EntityType is one of the following:
object: for entities derived from ElggObject user: for entities derived from ElggUser site: for entities derived from
ElggSite group: for entities derived from ElggGroup
A default view for all entities has already been created, this is called EntityType/default.
Entity Icons
Every entity can be assigned an icon which is retrieved using the ElggEntity::getIconURL($params)
method. This method accepts a $params argument that can be either a string specifying on of the configured icon
sizes, or an array of parameters, that specify the size and provide additional context for the hook to determine the icon
to serve.
Use elgg_get_config(icon_sizes) to get all possible values. The following sizes exist by default:
large, medium, small, tiny, and topbar. The method triggers the entity:icon:url hook.
Use elgg_view_entity_icon($entity, $size, $vars) to render an icon. This will scan the following
locations for a view and include the first match.
1. views/$viewtype/icon/$type/$subtype.php
2. views/$viewtype/icon/$type/default.php
3. views/$viewtype/icon/default.php
Where
$viewtype Type of view, e.g. default or json.
$type Type of entity, e.g. group or user.
$subtype Entity subtype, e.g. blog or page.
By convention entities that have an uploaded avatar or icon will have the icontime property assigned. This means
that you can use $entity->icontime to check if an icon exists for the given entity.
3.8. Database
83
To retrieve the ratings on the blog post, use $blogpost->getAnnotations(rating) and if you want to
delete an annotation, you can operate on the ElggAnnotation class, eg $annotation->delete().
Retrieving a single annotation can be done with get_annotation() if you have the annotations ID. If you delete
an ElggEntity of any kind, all its metadata, annotations, and relationships will be automatically deleted as well.
Extending ElggEntity
If you derive from one of the Elgg core classes, youll need to tell Elgg how to properly instantiate the new type of
object so that get_entity() et al. will return the appropriate PHP class. For example, if I customize ElggGroup in a
class called Committee, I need to make Elgg aware of the new mapping. Following is an example class extension:
// Class source
class Committee extends ElggGroup {
protected function initializeAttributes() {
parent::initializeAttributes();
$this->attributes['subtype'] = 'committee';
}
// more customizations here
}
function committee_init() {
register_entity_type('group', 'committee');
// Tell Elgg that group subtype "committee" should be loaded using the Committee class
// If you ever change the name of the class, use update_subtype() to change it
add_subtype('group', 'committee', 'Committee');
}
register_elgg_event_handler('init', 'system', 'committee_init');
Now if you invoke get_entity() with the GUID of a committee object, youll get back an object of type Committee.
This template was extracted from the definition of ElggFile.
Advanced features
Entity URLs
Entity urls are provided by the getURL() interface and provide the Elgg framework with a common way of directing
users to the appropriate display handler for any given object.
For example, a profile page in the case of users.
The url is set using the elgg\_register\_entity\_url\_handler() function. The function you register
must return the appropriate url for the given type - this itself can be an address set up by a page handler.
84
elgg_get_entities has a couple options that can sometimes be useful to improve performance.
preload_owners: If the entities fetched will be displayed in a list with the owner information, you can set this
option to true to efficiently load the owner users of the fetched entities.
preload_containers: If the entities fetched will be displayed in a list using info from their containers, you can
set this option to true to efficiently load them.
distinct: When Elgg fetches entities using an SQL query, Elgg must be sure that each entity row appears only
once in the result set. By default it includes a DISTINCT modifier on the GUID column to enforce this, but
some queries naturally return unique entities. Setting the distinct option to false will remove this modifier,
and rely on the query to enforce its own uniqueness.
The internals of Elgg entity queries is a complex subject and its recommended to seek help on the Elgg Community
site before using the distinct option.
Pre-1.8 Notes
update_subtype(): This function is new in 1.8. In prior versions, you would need to edit the database by hand if you
updated the class name associated with a given subtype.
elgg_register_entity_url_handler(): This function is new in 1.8. It deprecates register_entity_url_handler(), which you
should use if developing for a pre-1.8 version of Elgg.
elgg_get_entities_from_metadata(): This function is new in 1.8. It deprecates get_entities_from_metadata(), which
you should use if developing for a pre-1.8 version of Elgg.
my_plugin/sql/activate.sql:
-- Create some table
CREATE TABLE prefix_custom_table(
id INTEGER AUTO_INCREMENT,
name VARCHAR(32),
description VARCHAR(32),
PRIMARY KEY (id)
);
3.8. Database
85
Note that Elgg execute statements through PHPs built-in functions and have limited support for comments. I.e. only
single line comments are supported and must be prefixed by or # . A comment must start at the very beginning
of a line.
3.8.3 Systemlog
Note: This section need some attention and will contain outdated information
The default Elgg system log is a simple way of recording what happens within an Elgg system. Its viewable and
searchable directly from the administration panel.
System log storage
A system log row is stored whenever an event concerning an object whose class implements the Loggable interface
is triggered. ElggEntity and ElggExtender implement Loggable, so a system log row is created whenever an
event is performed on all objects, users, groups, sites, metadata and annotations.
Common events include:
create
update
delete
login
Creating your own system log
There are some reasons why you might want to create your own system log. For example, you might need to store
a full copy of entities when they are updated or deleted, for auditing purposes. You might also need to notify an
administrator when certain types of events occur.
To do this, you can create a function that listens to all events for all types of object:
register_elgg_event_handler('all','all','your_function_name');
You can then use the extra methods defined by Loggable to extract the information you need.
86
Permissions
By default, actions are only available to logged in users.
To make an action available to logged out users, pass "public" as the third parameter:
elgg_register_action("example", $filepath, "public");
To restrict an action to only administrators, pass "admin" for the last parameter:
87
You can then use the Database api to load entities and perform actions on them accordingly.
To redirect the page once youve completed your actions, use the forward function:
forward('url/to/forward/to');
Give feedback to the user about the status of the action by using system_message for positive feedback or
register_error for warnings and errors:
if ($success) {
system_message(elgg_echo(actions:example:success));
} else {
register_error(elgg_echo(actions:example:error));
}
Customizing actions
Before executing any action, Elgg triggers a hook:
$result = elgg_trigger_plugin_hook('action', $action, null, true);
Where $action is the action being called. If the hook returns false then the action will not be executed.
Example: Captcha
The captcha module uses this to intercept the register and user/requestnewpassword actions and redirect
them to a function which checks the captcha code. This check returns true if valid or false if not (which prevents
the associated action from executing).
This is done as follows:
88
This lets a plugin extend an existing action without the need to replace the whole action. In the case of the captcha
plugin it allows the plugin to provide captcha support in a very loosely coupled way.
To output a form, use the elgg_view_form function like so:
echo elgg_view_form('example');
Elgg does some things automatically for you when you generate forms this way:
1. It sets the action to the appropriate URL based on the name of the action you pass to it
2. It adds some anti-csrf tokens (__elgg_ts and __elgg_token) to help keep your actions secure
3. It automatically looks for the body of the form in the forms/example view.
Put the content of your form in your plugins forms/example view:
// /mod/example/views/default/forms/example.php
echo elgg_view('input/text', array('name' => 'example'));
echo elgg_view('input/submit');
89
In your action file, use the $_FILES global to access the uploaded file:
$icon = $_FILES[icon]
90
if (elgg_is_sticky_form('register')) {
extract(elgg_get_sticky_values('register'));
elgg_clear_sticky_form('register');
}
The registration action sets creates the sticky form and clears it once the action is completed:
// actions/register.php
elgg_make_sticky_form('register');
...
$guid = register_user($username, $password, $name, $email, false, $friend_guid, $invitecode);
if ($guid) {
elgg_clear_sticky_form('register');
....
}
Example: Bookmarks
The bundled plugin Bookmarks save form and action is an example of a complex sticky form.
The form view for the save bookmark action uses elgg_extract() to pull values from the $vars array:
// mod/bookmarks/views/default/forms/bookmarks/save.php
$title = elgg_extract('title', $vars, '');
$desc = elgg_extract('description', $vars, '');
$address = elgg_extract('address', $vars, '');
$tags = elgg_extract('tags', $vars, '');
$access_id = elgg_extract('access_id', $vars, ACCESS_DEFAULT);
$container_guid = elgg_extract('container_guid', $vars);
$guid = elgg_extract('guid', $vars, null);
$shares = elgg_extract('shares', $vars, array());
The page handler scripts prepares the form variables and calls elgg_view_form() passing the correct values:
// mod/bookmarks/pages/add.php
$vars = bookmarks_prepare_form_vars();
$content = elgg_view_form('bookmarks/save', array(), $vars);
Similarly, mod/bookmarks/pages/edit.php uses the same function, but passes the entity that is being edited
as an argument:
$bookmark_guid = get_input('guid');
$bookmark = get_entity($bookmark_guid);
...
$vars = bookmarks_prepare_form_vars($bookmark);
$content = elgg_view_form('bookmarks/save', array(), $vars);
91
The save action checks the input, then clears the sticky form upon success:
// mod/bookmarks/actions/bookmarks/save.php
elgg_make_sticky_form('bookmarks');
...
if ($bookmark->save()) {
elgg_clear_sticky_form('bookmarks');
}
3.9.4 Ajax
See the Ajax guide for instructions on calling actions from JavaScript.
3.9.5 Security
For enhanced security, all actions require an CSRF token. Calls to action URLs that do not include security tokens
will be ignored and a warning will be generated.
A few views and functions automatically generate security tokens:
92
Note: If you use a non-string as HMAC data, you must use types consistently. Consider the following:
$mac = elgg_build_hmac([123, 456])->getToken();
// type of first array element differs
elgg_build_hmac(["123", 456])->matchesToken($mac); // false
// types identical to original
elgg_build_hmac([123, 456])->matchesToken($mac); // true
93
3.10.4 Plugins
elgg_is_active_plugin($plugin_id) Check if a plugin is installed and enabled
94
3.11 Internationalization
Make your UI translatable into many different languages.
If youd like to contribute translations to Elgg, see the contributors guide.
3.11.1 Overview
Translations are stored in PHP files in the /languages directory of your plugin. Each file corresponds to a language.
The format is /languages/{language-code}.php where {language-code} is the ISO 639-1 short code
for the language. For example:
<?php
// mod/example/languages/en.php
return array(
example:text => Some example text,
);
Note: Unless you are overriding cores or another plugins language strings, it is good practice for the language keys
to start with your plugin name. For example: yourplugin:success, yourplugin:title, etc. This helps avoid conflicts
with other language keys.
3.11. Internationalization
95
echo elgg_echo(example:text);
To force which language should be used for translation, set the third parameter:
echo elgg_echo(welcome, array(), es);
Translations are also available after the init, system JavaScript event.
3.12 JavaScript
Contents
Third-party assets
AMD
Executing a module in the current page
Defining the Module
Making modules dependent on other modules
Passing plugin/Elgg settings to modules
Setting the URL of a module
Using traditional JS libraries as modules
Migrating JS from Elgg 1.8 to AMD / 1.9
Traditional JavaScript (1.8)
Core functions available in JS
Module elgg/spinner
Hooks
96
Note:
fxp/composer-asset-plugin
must
be
https://github.com/francoispluchino/composer-asset-plugin for more info.
installed
globally!
See
3.12.2 AMD
As of Elgg 1.9, we encourage all developers to adopt the AMD (Asynchronous Module Definition) standard for writing
JavaScript code in Elgg. The 1.8 version is still functional and is described below.
Here well describe making and executing AMD modules. The RequireJS documentation for defining modules may
also be of use.
Executing a module in the current page
Telling Elgg to load an existing module in the current page is easy:
<?php
elgg_require_js("myplugin/say_hello");
On the client-side, this will asynchronously load the module, load any dependencies, and execute the modules definition function, if it has one.
Defining the Module
Here we define a basic module that alters the page, by passing a definition function to define():
// in views/default/myplugin/say_hello.js
define(function(require) {
var elgg = require("elgg");
var $ = require("jquery");
$('body').append(elgg.echo('hello_world'));
});
The modules name is determined by the view name, which here is myplugin/say_hello.js. We strip the .js
extension, leaving myplugin/say_hello.
Warning: The definition function must have one argument named require.
3.12. JavaScript
97
= elgg_get_plugin_by_id('myplugin')->getAllSettings();
= [
=> elgg_extract('foo', $settings),
=> elgg_extract('bar', $settings),
?>
define(<?php echo json_encode($settings); ?>);
Note: The PHP view is cached, so you should treat the output as static (the same for all users) and avoid sessionspecific logic.
98
If youve copied the script directly into your plugin instead of managing it with Composer, you can use something like
this instead:
<?php // views.php
return [
'underscore.js' => __DIR__ . '/bower_components/underscore/underscore.min.js',
];
Thats it! Elgg will now load this file whenever the underscore module is requested.
Using traditional JS libraries as modules
Its possible to support JavaScript libraries that do not declare themselves as AMD modules (i.e. they declare global
variables instead) if you shim them by setting exports and deps in elgg_define_js:
// set the path, define its dependencies, and what value it returns
elgg_define_js('jquery.form', [
'deps' => ['jquery'],
'exports' => 'jQuery.fn.ajaxForm',
]);
is
1. Do not use elgg.provide() anymore nor other means to attach code to elgg or other global objects. Use
modules.
2. Return the value of the module instead of adding to a global variable.
3. Static (.js,.css,etc.) files are automatically minified and cached by Elggs simplecache system.
3.12. JavaScript
99
This will override any URLs previously registered under this name.
Load a library on the current page with elgg_load_js:
elgg_load_js('jquery');
elgg.system_message()
Display a status message to the user.
elgg.system_message(elgg.echo('success'));
elgg.register_error()
Display an error message to the user.
elgg.register_error(elgg.echo('error'));
elgg.forward()
elgg.normalize_url()
Normalize a URL relative to the elgg root:
// "http://localhost/elgg/blog"
elgg.normalize_url('/blog');
100
// returns {
//
fragment: "fragment",
//
host: "community.elgg.org",
//
path: "/file.php",
//
query: "arg=val"
// }
elgg.parse_url(
'http://community.elgg.org/file.php?arg=val#fragment');
elgg.get_page_owner_guid()
Get the GUID of the current pages owner.
elgg.register_hook_handler()
Register a hook handler with the event system.
// old initialization style
elgg.register_hook_handler('init', 'system', my_plugin.init);
// new: AMD module
define(function (require) {
var elgg = require('elgg');
// [init, system] has fired
});
elgg.trigger_hook()
Emit a hook event in the event system.
// allow other plugins to alter value
value = elgg.trigger_hook('my_plugin:filter', 'value', {}, value);
elgg.security.refreshToken()
Force a refresh of all XSRF tokens on the page.
This is automatically called every 5 minutes by default.
This requires a valid security token in 1.8, but not in 1.9.
The user will be warned if their session has expired.
elgg.security.addToken()
Add a security token to an object, URL, or query string:
// returns {
//
__elgg_token: "1468dc44c5b437f34423e2d55acfdd87",
//
__elgg_ts: 1328143779,
//
other: "data"
// }
elgg.security.addToken({'other': 'data'});
// returns: "action/add?__elgg_ts=1328144079&__elgg_token=55fd9c2d7f5075d11e722358afd5fde2"
elgg.security.addToken("action/add");
// returns "?arg=val&__elgg_ts=1328144079&__elgg_token=55fd9c2d7f5075d11e722358afd5fde2"
elgg.security.addToken("?arg=val");
elgg.get_logged_in_user_entity()
3.12. JavaScript
101
Module elgg/spinner
The elgg/spinner module can be used to create an Ajax loading indicator fixed to the top of the window.
define(function (require) {
var spinner = require('elgg/spinner');
elgg.action('friend/add', {
beforeSend: spinner.start,
complete: spinner.stop,
success: function (json) {
// ...
}
});
});
Hooks
The JS engine has a hooks system similar to the PHP engines plugin hooks: hooks are triggered and plugins can
register callbacks to react or alter information. There is no concept of Elgg events in the JS engine; everything in the
JS engine is implemented as a hook.
Registering a callback to a hook
Callbacks are registered using elgg.register_hook_handler(). Multiple callbacks can be registered for the
same hook.
102
The following example registers the elgg.ui.initDatePicker callback for the init, system event. Note
that a difference in the JS engine is that instead of passing a string you pass the function itself to
elgg.register_hook_handler() as the callback.
elgg.provide('elgg.ui.initDatePicker');
elgg.ui.initDatePicker = function() { ... }
elgg.register_hook_handler('init', 'system', elgg.ui.initDatePicker);
The callback
Available hooks
init, system This hook is fired when the JS system is ready. Plugins should register their init functions for this hook.
ready, system This hook is fired when the system has fully booted.
getOptions, ui.popup This hook is fired for pop up displays (rel=popup) and allows for customized placement
options.
3.13 Menus
Elgg contains helper code to build menus throughout the site.
Every single menu requires a name, as does every single menu item. These are required in order to allow easy
overriding and manipulation, as well as to provide hooks for theming.
Contents
Basic usage
Advanced usage
Creating a new menu
Theming
3.13. Menus
103
/**
* Initialize the plugin
*/
function my_plugin_init() {
// Register a plugin hook handler for the owner_block menu
elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'my_owner_block_menu_handle
}
/**
* Change the URL of the "Albums" menu item in the owner_block menu
*/
function my_owner_block_menu_handler($hook, $type, $menu, $params) {
$owner = $params['entity'];
104
3.13. Menus
105
}
}
return $menu;
}
You can now add new items to the menu like this:
elgg_register_menu_item('my_menu', array(
'name' => 'my_page',
'href' => 'path/to/my_page',
'text' => elgg_echo('my_plugin:my_page'),
));
Furthermore it is now possible to modify the menu using the hooks register, menu:my_menu and
prepare, menu:my_menu.
3.13.4 Theming
The menu name, section names, and item names are all embedded into the HTML as CSS classes (normalized to
contain only hyphens, rather that underscores or colons). This increases the size of the markup slightly but provides
themers with a high degree of control and flexibility when styling the site.
Example: The following would be the output of the foo menu with sections alt and default containing items
baz and bar respectively.
<ul class="elgg-menu elgg-menu-foo elgg-menu-foo-alt">
<li class="elgg-menu-item elgg-menu-item-baz"></li>
</ul>
<ul class="elgg-menu elgg-menu-foo elgg-menu-foo-default">
<li class="elgg-menu-item elgg-menu-item-bar"></li>
</ul>
3.14 Notifications
There are two ways to send notifications in Elgg:
Instant notifications
Event-based notifications send using a notifications queue
106
Contents
Instant notifications
Enqueued notifications
Registering a new notification method
Sending the notifications using your own method
Subscriptions
Note: The language used by the recipient isnt necessarily the same as the language of the person who triggers
the notification. Therefore you must always remember to pass the recipients language as the third parameter to
elgg_echo().
Note: The summary parameter is meant for notification plugins that only want to display a short message instead
of both the subject and the body. Therefore the summary should be terse but still contain all necessary information.
3.14. Notifications
107
Note: In order to send the event-based notifications you must have the one-minute CRON interval configured.
Contents
of
the
notification
message
can
be
notification:[action]:[type]:[subtype] hook.
defined
with
the
prepare,
Example
Tell Elgg to use the function photos_prepare_notification() to format the contents of the notification
when a new objects of subtype photo is created:
/**
* Initialize the photos plugin
*/
function photos_init() {
elgg_register_notification_event('object', 'photo', array('create'));
elgg_register_plugin_hook_handler('prepare', 'notification:create:object:photo', 'photos_prepare_
}
/**
* Prepare a notification message about a new photo
*
$hook
Hook name
* @param string
$type
Hook type
* @param string
* @param Elgg_Notifications_Notification $notification The notification to prepare
$params
Hook parameters
* @param array
* @return Elgg_Notifications_Notification
*/
function photos_prepare_notification($hook, $type, $notification, $params) {
$entity = $params['event']->getObject();
$owner = $params['event']->getActor();
$recipient = $params['recipient'];
$language = $params['language'];
$method = $params['method'];
// Title for the notification
$notification->subject = elgg_echo('photos:notify:subject', array($entity->title), $language);
108
Note: Make sure the notification will be in the correct language by passing the reciepients language into the
elgg_echo() function.
notification
settings
page
at
3.14. Notifications
109
3.14.5 Subscriptions
In most cases Elgg core takes care of handling the subscriptions, so notification plugins dont usually have to alter
them.
Subscriptions can however be:
Added using the elgg_add_subscription() function
Removed using the elgg_remove_subscription() function
Its possible to modify the recipients of a notification dynamically with the get, subscriptions hook.
Example:
/**
* Initialize the plugin
*/
function discussion_init() {
elgg_register_plugin_hook_handler('get', 'subscriptions', 'discussion_get_subscriptions');
}
/**
* Get subscriptions for group notifications
*
'get'
* @param string $hook
'subscriptions'
* @param string $type
* @param array $subscriptions Array containing subscriptions in the form
<user guid> => array('email', 'site', etc.)
*
Hook parameters
* @param array $params
* @return array
*/
110
3.16 Routing
Elgg has two mechanisms to respond to HTTP requests that dont already go through the Actions and Simplecache
systems.
111
The site URL (home page) is a special case that produces an empty string identifier and an empty segments array.
112
The following code results in /blog/all requests being completely handled by the plugin hook handler. For these
requests the blog page handler is never called.
function myplugin_blog_all_handler($hook, $type, $returnvalue, $params) {
$segments = elgg_extract('segments', $returnvalue, array());
if (isset($segments[0]) && $segments[0] === 'all') {
$title = "We're taking over!";
$content = elgg_view_layout('one_column', array(
'title' => $title,
'content' => "We can take over page rendering completely"
));
echo elgg_view_page($title, $content);
// in the route hook, return false says, "stop rendering, we've handled this request"
return false;
}
}
elgg_register_plugin_hook_handler('route', 'blog', 'myplugin_blog_all_handler');
It calls
7. The resources/blog/owner view gets the username via $vars[username], and uses many other
views and formatting functions like elgg_view_layout() and elgg_view_page() to create the entire
HTML page.
8. The page handler echos the view HTML and returns true to indicate it handled the request.
9. PHP invokes Elggs shutdown sequence.
10. The user receives a fully rendered page.
Elggs coding standards suggest a particular URL layout, but there is no syntax enforced.
3.17 Services
Elgg uses the Elgg\Application class to load and bootstrap Elgg. In future releases this class will offer a set of
service objects for plugins to use.
Note: If you have a useful idea, you can add a new service!
3.17. Services
113
114
true if the entity has write access, false if the entity does not, and null if this plugin doesnt care and the security system
should consult other plugins.
function myplugin_permissions_check($hook_name, $entity_type, $return_value, $parameters) {
$has_access = determine_access_somehow();
if ($has_access === true) {
return true;
} else if ($has_access === false) {
return false;
}
return null;
}
115
// Initialise plugin
register_elgg_event_handler('init', 'system', 'myaccess_init');
?>
<select name="params[limit]">
<option value="5" <?php if ($vars['entity']->limit == 5) echo " selected=\"yes\" "; ?>>5</optio
<option value="8" <?php if ((!$vars['entity']->limit) || ($vars['entity']->limit == 8)) echo "
<option value="12" <?php if ($vars['entity']->limit == 12) echo " selected=\"yes\" "; ?>>12</op
<option value="15" <?php if ($vars['entity']->limit == 15) echo " selected=\"yes\" "; ?>>15</op
</select>
</p>
Note: You dont need to add a save button or the form, this will be handled by the framework.
Note: You cannot use form components that send no value when off. These include radio inputs and check boxes.
116
where:
$name Is the value you want to retrieve
$user_guid Is the user you want to retrieve these for (defaults to the currently logged in user)
$plugin_name Is the name of the plugin (detected if run from within a plugin)
or
elgg_set_plugin_user_setting($name, $value, $user_guid, $plugin_id);
3.21 River
Elgg natively supports the river, an activity stream containing descriptions of activities performed by site members.
This page gives an overview of adding events to the river in an Elgg plugin.
3.21. River
117
target_guid => INT The GUID of the the object entitys container (optional)
access_id => INT The access ID of the river item (default: same as the object)
posted => INT The UNIX epoch timestamp of the river item (default: now)
annotation_id => INT The annotation ID associated with this river entry (optional)
When an item is deleted or changed, the river item will be updated automatically.
3.22 Routing
Elgg has two mechanisms to respond to HTTP requests that dont already go through the Actions and Simplecache
systems.
118
The site URL (home page) is a special case that produces an empty string identifier and an empty segments array.
The following code results in /blog/all requests being completely handled by the plugin hook handler. For these
requests the blog page handler is never called.
3.22. Routing
119
It calls
7. The resources/blog/owner view gets the username via $vars[username], and uses many other
views and formatting functions like elgg_view_layout() and elgg_view_page() to create the entire
HTML page.
8. The page handler echos the view HTML and returns true to indicate it handled the request.
9. PHP invokes Elggs shutdown sequence.
10. The user receives a fully rendered page.
Elggs coding standards suggest a particular URL layout, but there is no syntax enforced.
3.23 Themes
Customize the look and feel of Elgg.
A theme is a type of plugin that overrides display aspects of Elgg.
This guide assumes you are familiar with:
Plugins
Views
120
Contents
Create your plugin
Customize the CSS
View extension
View overloading
Icons
Tools
Customizing the front page
121
View extension
There are two ways you can modify views:
The first way is to add extra stuff to an existing view via the extend view function from within your start.phps
initialization function.
For example, the following start.php will add mytheme/css to Elggs core css file:
<?php
function mytheme_init() {
elgg_extend_view('elgg.css', 'mytheme/css');
}
elgg_register_event_handler('init', 'system', 'mytheme_init');
?>
View overloading
Plugins can have a view hierarchy, any file that exists here will replace any files in the existing core view hierarchy...
so for example, if my plugin has a file:
/mod/myplugin/views/default/elements/typography.css
it will replace:
/views/default/elements/typography.css
But only when the plugin is active.
This gives you total control over the way Elgg looks and behaves. It gives you the option to either slightly modify or
totally replace existing views.
Icons
As of Elgg 2.0 the default Elgg icons come from the FontAwesome library. You can use any of these icons by calling:
elgg_view_icon(icon-name);
icon-name can be any of the FontAwesome icons without the fa--prefix.
3.23.3 Tools
Starting in Elgg 1.8, weve provided you with some development tools to help you with theming: Turn on the Developers plugin and go to the Theme Preview page to start tracking your themes progress.
Then, create an index page (/pluginname/pages/index.php) and use that to put the content you would like on the
front page of your Elgg site.
3.24 Views
Contents
Introduction
Using views
Views as templates
Views as cacheable assets
Views and third-party assets
Viewtypes
Altering views via plugins
Displaying entities
Full and partial entity views
Listing entities
Related
3.24.1 Introduction
Views are responsible for creating output. They handle everything from:
the layout of pages
chunks of presentation output (like a footer or a toolbar)
individual links and form inputs.
the images, js, and css needed by your web page
3.24. Views
123
For your convenience, Elgg comes with quite a lot of views by default. In order to keep things manageable, they are
organized into subdirectories. Elgg handles this situation quite nicely. For example, our simple view might live in
/views/default/hello/world.php, in which case it would be called like so:
echo elgg_view('hello/world');
The name of the view simply reflects the location of the view in the views directory.
In this case, we can pass an arbitrary name parameter to the view like so:
echo elgg_view('hello/world', ['name' => 'World']);
Warning: Views dont do any kind of automatic output sanitization by default. You are responsible for doing the
correct sanitization yourself to prevent XSS attacks and the like.
124
/* /views/default/mystyles.css */
.mystyles-foo {
background: red;
}
Note: Leave off the trailing .php from the filename and Elgg will automatically recognize the view as cacheable.
To get a URL to this file, you would use elgg_get_simplecache_url:
// Returns "https://mysite.com/.../289124335/default/mystyles.css
elgg_get_simplecache_url('mystyles.css');
Elgg automatically adds the magic numbers you see there for cache-busting and sets long-term expires headers on the
returned file.
Warning: Elgg may decide to change the location or structure of the returned URL in a future release for whatever
reason, and the cache-busting numbers change every time you flush Elggs caches, so the exact URL is not stable
by design.
With that in mind, heres a couple anti-patterns to avoid:
Dont rely on the exact structure/location of this URL
Dont try to generate the URLs yourself
Dont store the returned URLs in a database
In your plugins init function, register the css file:
elgg_register_css('mystyles', elgg_get_simplecache_url('mystyles.css'));
To point to assets installed with fxp/composer-asset-plugin, use install-root-relative paths by leaving off the
leading slash:
<?php // mod/example/views.php
return [
// viewtype
3.24. Views
125
'default' => [
// view => path/from/install/root
'js/jquery-ui.js' => 'vendor/bower-asset/jquery-ui/jquery-ui.min.js',
],
];
With the above, files found within the icons folder will be interpreted as views.
E.g.
the view
file/icon/general.gif will be created and mapped to mod/file/graphics/icons/general.gif.
Note: This is a fully recursive scan. All files found will be brought into the views system.
3.24.6 Viewtypes
You might be wondering:
/views/hello/world.php?.
Why
/views/default/hello/world.php
instead
of
just
The subdirectory under /views determines the viewtype of the views below it. A viewtype generally corresponds to
the output format of the views.
The default viewtype is assumed to be HTML and other static assets necessary to render a responsive web page in a
desktop or mobile browser, but it could also be:
RSS
ATOM
JSON
Mobile-optimized HTML
TV-optimized HTML
Any number of other data formats
You can force Elgg to use a particular viewtype to render the page by setting the view input variable like so:
https://mysite.com/?view=rss.
You could also write a plugin to set this automatically using the elgg_set_viewtype() function. For example,
your plugin might detect that the page was accessed with an iPhones browser string, and set the viewtype to iphone
by calling:
126
elgg_set_viewtype('iphone');
The plugin would presumably also supply a set of views optimized for those devices.
Note: When considering long-term maintenance, overriding views in the core and bundled plugins has a cost: Upgrades may bring changes in views, and if you have overridden them, you will not get those changes.
You may instead want to alter the input or the output of the view via plugin hooks.
Note: Elgg caches view locations. This means that you should disable the system cache while developing with views.
When you install the changes to a production environment you must flush the caches.
Extending views
There may be other situations in which you dont want to override the whole view, you just want to prepend or append
some more content to it. In Elgg this is called extending a view.
For example, instead of overriding the hello/world view, we could extend it like so:
elgg_extend_view('hello/world', 'hello/greeting');
You can prepend views by passing a value to the 3rd parameter that is less than 500:
3.24. Views
127
All view extensions should be registered in your plugins init,system event handler in start.php.
Altering view input
It may be useful to alter a views $vars array before the view is rendered.
Since 1.11, before each view rendering the $vars array is filtered by the plugin hook ["view_vars",
$view_name]. Each registered handler function is passed these arguments:
$hook - the string "view_vars"
$view_name - the view name being rendered (the first argument passed to elgg_view())
$returnvalue - the modified $vars array
$params - an array containing:
vars - the original $vars array, unaltered
view - the view name
viewtype - The viewtype being rendered
Altering view input example
Here well alter the default pagination limit for the comments view:
128
Here well eliminate breadcrumbs that dont have at least one link.
elgg_register_plugin_hook_handler('view', 'navigation/breadcrumbs', 'myplugin_alter_breadcrumb');
function myplugin_alter_breadcrumb($hook, $type, $returnvalue, $params) {
// we only want to alter when viewtype is "default"
if ($params['viewtype'] !== 'default') {
return $returnvalue;
}
// output nothing if the content doesn't have a single link
if (false === strpos($returnvalue, '<a ')) {
return '';
}
// returning nothing means "don't alter the returnvalue"
}
As youll know from the data model introduction, all entities have a type (object, site, user or group), and optionally a
subtype (which could be anything - blog, forumpost, banana).
elgg_view_entity will automatically look for a view called type/subtype; if theres no subtype, it will look
for type/type. Failing that, before it gives up completely it tries type/default.
RSS feeds in Elgg generally work by outputting the object/default view in the rss viewtype.
For example, the view to display a blog post might be object/blog. The view to display a user is user/default.
3.24. Views
129
echo elgg_list_entities([
'type' => 'object',
'subtype' => 'blog',
]);
This function checks to see if there are any entities; if there are, it first displays the navigation/pagination
view in order to display a way to move from page to page. It then repeatedly calls elgg_view_entity on each
entity before returning the result.
Note that elgg_list_entities allows the URL to set its limit and offset options, so set those explicitly if
you need particular values (e.g. if youre not using it for pagination).
Elgg knows that it can automatically supply an RSS feed on pages that use elgg_list_entities. It initializes
the ["head","page"] plugin hook (which is used by the header) in order to provide RSS autodiscovery, which is
why you can see the orange RSS icon on those pages in some browsers.
If your entity list will display the entity owners, you can improve performance a bit by preloading all owner entities:
echo elgg_list_entities([
'type' => 'object',
'subtype' => 'blog',
// enable owner preloading
'preload_owners' => true,
]);
In the first example, we are displaying a list of groups a user is a member of using the default group view. In the
second example, we want to display a list of groups the user was invited to.
Since invitations are not entities, they do not have their own views and can not be listed using elgg_list_*. We
are providing an alternative item view, that will use the group entity to display an invitation that contains a group name
and buttons to access or reject the invitation.
130
3.24.11 Related
Page structure best practice
Elgg pages have an overall pageshell and a main content area. In Elgg 1.0+, weve marked out a space the canvas
for items to write to the page. This means the user always has a very consistent experience, while giving maximum
flexibility to plugin authors for laying out their functionality.
Think of the canvas area as a big rectangle that you can do what you like in. Weve created a couple of standard
canvases for you:
one column
two column
content
widgets
are the main ones. You can access these with the function:
$canvas_area = elgg_view_layout($canvas_name, array(
'content' => $content,
'section' => $section
));
The content sections are passed as an array in the second parameter. The array keys correspond to sections in the
layout, the choice of layout will determine which sections to pass. The array values contain the html that should be
displayed in those areas. Examples of two common layouts:
$canvas_area = elgg_view_layout('one_column', array(
'content' => $content
));
$canvas_area = elgg_view_layout('one_sidebar', array(
'content' => $content,
'sidebar' => $sidebar
));
You can then, ultimately, pass this into the elgg_view_page function:
echo elgg_view_page($title, $canvas_area);
You may also have noticed that weve started including a standard title area at the top of each plugin page (or at least,
most plugin pages). This is created using the following wrapper function, and should usually be included at the top of
the plugin content:
$start_of_plugin_content = elgg_view_title($title_text);
This will also display any submenu items that exist (unless you set the second, optional parameter to false). So how
do you add submenu items?
In your plugin_init function, include the following call:
if (elgg_get_context() == "your_plugin") {
// add a site navigation item
$item = new ElggMenuItem('identifier', elgg_echo('your_plugin:link'), $url);
elgg_register_menu_item('page', $item);
}
The submenu will then automatically display when your page is rendered. The identifier is a machine name for the
link, it should be unique per menu.
3.24. Views
131
Simplecache
See also:
Performance
Views
The Simplecache is a mechanism designed to alleviate the need for certain views to be regenerated dynamically.
Instead, they are generated once, saved as a static file, and served in a way that entirely bypasses the Elgg engine.
If Simplecache is turned off (which can be done from the administration panel), these views will be served as normal,
with the exception of site CSS.
The criteria for whether a view is suitable for the Simplecache is as follows:
The view must not change depending on who or when it is being looked at
The view must not depend on variables fed to it (except for global variables like site URL that never change)
Regenerating the Simplecache
Page/elements/foot vs footer
page/elements/footer is the content that goes inside this part of the page:
<div class="elgg-page-footer">
<div class="elgg-inner">
<!-- page/elements/footer goes here -->
</div>
</div>
132
Its content is visible to end users and usually where you would put a sitemap or other secondary global navigation,
copyright info, powered by elgg, etc.
page/elements/foot is inserted just before the ending </body> tag and is mostly meant as a place to insert scripts that dont already work with elgg_register_js(array(location => footer)); or
elgg_require_js(amd/module);. In other words, you should never override this view and probably dont
need to extend it either. Just use the elgg_*_js functions instead
3.25 Widgets
Widgets are content areas that users can drag around their page to customize the layout. They can typically be
customized by their owner to show more/less content and determine who sees the widget. By default Elgg provides
plugins for customizing the profile page and dashboard via widgets.
TODO: Screenshot
Contents
Structure
Initialise the widget
Multiple widgets
Elgg 1.8: Default widgets
Simple Example
How to restrict where widgets can be used
Find where the plugin registers the widget
Changing the functions parameters
3.25.1 Structure
To create a widget, create two views:
widgets/widget/edit
widgets/widget/content
content.php is responsible for all the content that will output within the widget. The edit.php file contains any
extra edit functions you wish to present to the user. You do not need to add access level as this comes as part of the
widget framework.
Note: Using HTML checkboxes to set widget flags is problematic because if unchecked, the checkbox input is
omitted from form submission. The effect is that you can only set and not clear flags. The input/checkboxes view
will not work properly in a widgets edit panel.
3.25. Widgets
133
Note: It is possible to add multiple widgets for a plugin. You just initialize as many widget directories as you need.
// Add generic new file widget
add_widget_type('filerepo', elgg_echo("file:widget"), elgg_echo("file:widget:description"));
// Add a second file widget
add_widget_type('filerepo2', elgg_echo("file:widget2"), elgg_echo("file:widget:description2"));
// Add a third file widget
add_widget_type('filerepo3', elgg_echo("file:widget3"), elgg_echo("file:widget:description3"));
In the plugin hook handler, push an array into the return value defining your default widget support and when to create
default widgets. Arrays require the following keys to be defined:
name - The name of the widgets page. This is displayed on the tab in the admin interface.
widget_context - The context the widgets page is called from. (If not explicitly set, this is your plugins id.)
widget_columns - How many columns the widgets page will use.
event - The Elgg event to create new widgets for. This is usually create.
entity_type - The entity type to create new widgets for.
entity_subtype - The entity subtype to create new widgets for. The can be ELGG_ENTITIES_ANY_VALUE to
create for all entity types.
When an object triggers an event that matches the event, entity_type, and entity_subtype parameters passed, Elgg
core will look for default widgets that match the widget_context and will copy them to that objects owner_guid and
container_guid. All widget settings will also be copied.
134
<p>
<?php echo elgg_echo("flickr:id"); ?>
<input type="text" name="params[title]" value="<?php echo htmlentities($vars['entity']->title); ?
</p>
<p><?php echo elgg_echo("flickr:whatisid"); ?></p>
?>
<!-- this script uses the jquery cycle plugin -->
<script type="text/javascript" src="<?php echo $vars['url']; ?>mod/flickr/views/default/flickr/js/cyc
<!-- the Flickr JSON script -->
<script>
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?id=
<?php echo $flickr_id;?>&lang=en-us&format=json&jsoncallback=?", function(data){
$.each(data.items, function(i,item){
$("<img/>").attr("src", item.media.m).appendTo("#images")
.wrap("<a href='" + item.link + "'></a>");
});
$('#images').cycle({
fx:
'fade',
speed:
'slow',
timeout: 0,
next:
'#next',
prev:
'#prev'
});
3.25. Widgets
135
});
</script>
<!-- some css for display -->
<style type="text/css">
#images {
height: 180px;
width: 100%;
padding:0;
margin:0 0 10px 0;
overflow: hidden;
}
#images img {
border:none;
}
</style>
<!-- div where the images will display -->
<div id="title"></div>
<div id="images" align="center"></div>
<!-- next and prev nav -->
<div class="flickrNav" align="center">
<a id="prev" href="#">« Prev</a> <a id="next" href="#">Next »</a>
</div>
<?php
}else{
//this should go through elgg_echo() - it was taken out for this demo
echo "You have not yet entered your Flickr ID which is required to display your photos.";
}
?>
136
Notice that the context was not specified originally (there were only 3 parameters and we added a 4th). That means
it defaulted to the all context. Besides all and profile, the only other context available in default Elgg is dashboard.
137
3.27.1 Security
It is crucial that the web services are consumed via secure protocols. Do not enable web services if your site is not
served via HTTPs. This is especially important if you allow API key only authentication.
If you are using third-party tools that expose API methods, make sure to carry out a thorough security audit. You
may want to make sure that API authentication is required for ALL methods, even if they require user authentication.
Methods that do not require API authentication can be easily abused to spam your site.
Ensure that the validity of API keys is limited and provide mechanisms for your API clients to renew their keys.
Since we are providing this function to allow developers to test their API clients, we will require neither API authentication nor user authentication. This call registers the function with the web services API framework:
elgg_ws_expose_function("test.echo",
"my_echo",
array("string" => array('type' => 'string')),
'A testing method which echos back a string',
'GET',
false,
138
false
);
Response formats
The web services API framework provides three different response formats by default: xml, json, and serialized php.
You can request the different formats for substituting json or php for xml in the above URLs. You can also add
additional response formats by defining new viewtypes.
Parameters
Parameters expected by each method should be listed as an associative array, where the key represents the parameter
name, and the value contains an array with type, default and required fields.
Values submitted with the API request for each parameter should match the declared type. API will throw on exception
if validation fails.
Recognized parameter types are:
integer (or int)
boolean (or bool)
string
float
array
Unrecognized types will throw an API exception.
You can use additional fields to describe your parameter, e.g. description.
elgg_ws_expose_function('test.greet',
'my_greeting',
array(
'name' => array(
'type' => 'string',
'required' => true,
'description' => 'Name of the person to be greeted by the API',
),
'greeting' => array(
'type' => 'string',
'required' => false,
'default' => 'Hello',
'description' => 'Greeting to be used, e.g. "Good day" or "Hi"',
),
),
'A testing method which greets the user with a custom greeting',
139
'GET',
false,
false
);
Now, lets expose it and make the number of minutes an optional parameter:
elgg_ws_expose_function("users.active",
"count_active_users",
array("minutes" => array('type' => 'int',
'required' => false)),
'Number of users who have used the site in the past x minutes',
'GET',
true,
false
);
This function is now available and if you check system.api.list, you will see that it requires API authentication. If you hit the method with a web browser, it will return an error message about failing the API
authentication. To test this method, you need an API key. Fortunately, there is a plugin called apiadmin that
creates keys for you. It is available in the Elgg plugin repository. It will return a public and private key and you
will use the public key for this kind of API authentication. Grab a key and then do a GET request with your
browser on this API method passing in the key string as the parameter api_key. It might look something like this:
http://yoursite.com/services/api/rest/xml/?method=users.active&api_key=1140321cb56c71710c38feefdf72bc462938f59f.
Signature-based authentication
The HMAC Authentication is similar to what is used with OAuth or Amazons S3 service. This involves both the
public and private key. If you want to be very sure that the API calls are coming from the developer you think they
are coming from and you want to make sure the data is not being tampered with during transmission, you would use
140
this authentication method. Be aware that it is much more involved and could turn off developers when there are other
sites out there with key-based authentication.
OAuth
With the addition of the OAuth plugin, Elgg also fully supports the OAuth 1.0a authorization standard. Clients can
then use standard OAuth libraries to make any API calls to the site.
Exposing this function is the same as the previous except we require user authentication and were going to make this
use POST rather than GET HTTP requests.
elgg_ws_expose_function("thewire.post",
"my_post_to_wire",
array("text" => array('type' => 'string')),
'Post to the wire. 140 characters or less',
'POST',
true,
true
);
Please note that you will not be able to test this using a web browser as you did with the other methods. You need to
write some client code to do this. There is some example client code in /engine/lib/api.php. Take a look at
send_api_post_call(). You can also do a search for clients that have been written for the APIs of Flickr or Twitter or
any other similar API. You will find a wide variety written in almost any language you can think of.
141
Then in the callback function, you register the PAMs that you want to use:
function rest_plugin_setup_pams() {
// user token can also be used for user authentication
register_pam_handler('pam_auth_usertoken');
// simple API key check
register_pam_handler('api_auth_key', "sufficient", "api");
// override the default pams
return true;
}
When testing, you may find it useful to register the pam_auth_session PAM so that you can easily test your
methods from the browser. Be careful not to use this PAM on a production site because it could open up your users to
a CSRF attack.
Right now, the only other PAMs publicly available besides those provided by the Elgg core are the OAuth PAMs. See
Justin Richers OAuth plugin for more detail.
3.27.7 Related
HMAC Authentication
Elggs RESTful API framework provides functions to support a HMAC signature scheme for API authentication. The
client must send the HMAC signature together with a set of special HTTP headers when making a call that requires
API authentication. This ensures that the API call is being made from the stated client and that the data has not been
tampered with.
The HMAC must be constructed over the following data:
The public API key identifying you to the Elgg api server as provided by the APIAdmin plugin
The private API Key provided by Elgg (that is companion to the public key)
The current unix time in seconds
A nonce to guarantee two requests the same second have different signatures
URL encoded string representation of any GET variable parameters, eg method=test.test&foo=bar
If you are sending post data, the hash of this data
Some extra information must be added to the HTTP header in order for this data to be correctly processed:
X-Elgg-apikey - The public API key
X-Elgg-time - Unix time used in the HMAC calculation
X-Elgg-none - a random string
X-Elgg-hmac - The HMAC as base64 encoded
142
X-Elgg-hmac-algo - The algorithm used in the HMAC calculation - eg, sha1, md5 etc.
If you are sending POST data you must also send:
X-Elgg-posthash - The hash of the POST data
X-Elgg-posthash-algo - The algorithm used to produce the POST data hash - eg, md5
Content-type - The content type of the data you are sending (if in doubt use application/octet-stream)
Content-Length - The length in bytes of your POST data
Elgg provides a sample API client that implements this HMAC signature: send_api_call(). It serves as a good reference
on how to implement it.
143
Contents
From 1.x to 2.0
Elgg can be now installed as a composer dependency instead of at document root
Cacheable views must have a file extension in their names
Dropped jquery-migrate and upgraded jquery to ^2.1.4
JS and CSS views have been moved out of the js/ and css/ directories
fxp/composer-asset-plugin is now required to install Elgg from source
List of deprecated views and view arguments that have been removed
All scripts moved to bottom of page
Attribute formatter removes keys with underscores
Breadcrumbs
Callbacks in Queries
Comments plugin hook
Creating a relationship triggers only one event
Discussion feature has been pulled from groups into its own plugin
Dropped login-over-https feature
Elgg has migrated from ext/mysql to PDO MySQL
Event/Hook calling order may change
export/ URLs are no longer available
Icons migrated to Font Awesome
Introduced third-party library for sending email
Label elements
Plugin Messages
Plugin Blog
Plugin Bookmarks
Plugin File
Removed Classes
Removed keys available via elgg_get_config()
Removed Functions
Removed methods
Removed Plugin Hooks
Removed Actions
Removed Views
Removed View Variables
Removed libraries
Specifying View via Properties
Viewtype is static after the initial elgg_get_viewtype() call
From 1.10 to 1.11
Comment highlighting
From 1.9 to 1.10
File uploads
From 1.8 to 1.9
The manifest file
$CONFIG and $vars[config]
Language files
Notifications
Adding items to the Activity listing
Entity URL handlers
Web services
From 1.7 to 1.8
Updating core
Updating plugins
144
elgg_get_root_path and $CONFIG->path will return the path to the application root directory (the one containing settings.php), which is not necessarily the same as Elgg cores root directory (which in this case is
vendor/elgg/elgg/).
Do not attempt to access the core Elgg from your plugin directly, since you cannot rely on its location on the filesystem.
In particular, dont try load engine/start.php.
// Don't do this!
dirname(__DIR__) . "/engine/start.php";
However, use this approach sparingly. Prefer Routing instead whenever possible as that keeps your public URLs and
your filesystem layout decoupled.
Also, dont try to access the _graphics files directly.
readfile(elgg_get_root_path() . "_graphics/elgg_sprites.png");
145
New view
view.js
other.js
view.css
other.css
img.png
The main benefit this brings is being able to co-locate related assets. So a template (view.php) can have its CSS/JS
dependencies right next to it (view.css, view.js).
Care has been taken to make this change as backwards-compatible as possible, so you should not need to update any
view references right away. However, you are certainly encouraged to move your JS and CSS views to their new,
canonical locations.
Practically speaking, this carries a few gotchas:
The view_vars, $view_name and view, $view_name hooks will operate on the canonical view name:
elgg_register_plugin_hook_handler('view', 'css/elgg', function($hook, $view_name) {
assert($view_name == 'elgg.css') // not "css/elgg"
});
Using the view, all hook and checking for individual views may not work as intended:
elgg_register_plugin_hook_handler('view', 'all', function($hook, $view_name) {
// Won't work because "css/elgg" was aliased to "elgg.css"
if ($view_name == 'css/elgg') {
// Never executed...
}
// Won't work because no canonical views start with css/* anymore
if (strpos($view_name, 'css/') === 0) {
// Never executed...
}
});
Please let us know about any other BC issues this change causes. Wed like to fix as many as possible to make the
transition smooth.
146
If you dont do this before running composer install or composer create-project, you will get an error
message:
[InvalidArgumentException]
Package fxp/composer-asset-plugin not found
List of deprecated views and view arguments that have been removed
We dropped support for and/or removed the following views:
canvas/layouts/*
categories
categories/view
core/settings/tools
embed/addcontentjs
footer/analytics (Use page/elements/foot instead)
groups/left_column
groups/right_column
groups/search/finishblurb
groups/search/startblurb
input/calendar (Use input/date instead)
input/datepicker (Use input/date instead)
input/pulldown (Use input/select instead)
invitefriends/formitems
js/initialise_elgg (Use AMD and elgg_require_js instead of extending JS views)
members/nav
metatags (Use the head, page plugin hook instead)
navigation/topbar_tools
navigation/viewtype
notifications/subscriptions/groupsform
object/groupforumtopic
output/calendar (Use output/date instead)
output/confirmlink (Use output/url instead)
page_elements/contentwrapper
147
148
Breadcrumbs
Breadcrumb display now removes the last item if it does not contain a link. To restore the previous behavior, replace
the plugin hook handler elgg_prepare_breadcrumbs with your own:
elgg_unregister_plugin_hook_handler('prepare', 'breadcrumbs', 'elgg_prepare_breadcrumbs');
elgg_register_plugin_hook_handler('prepare', 'breadcrumbs', 'myplugin_prepare_breadcrumbs');
function myplugin_prepare_breadcrumbs($hook, $type, $breadcrumbs, $params) {
// just apply excerpt to titles
foreach (array_keys($breadcrumbs) as $i) {
$breadcrumbs[$i]['title'] = elgg_get_excerpt($breadcrumbs[$i]['title'], 100);
}
return $breadcrumbs;
}
Callbacks in Queries
Make sure to use only valid callable values for callback argument/options in the API.
Querying functions will now will throw a RuntimeException if is_callable() returns false for the given
callback value. This includes functions such as elgg_get_entities(), get_data(), and many more.
Comments plugin hook
Plugins can now return an empty string from comments,$entity_type hook in order to override the default
comments component view. To force the default comments component, your plugin must return false. If you were
using empty strings to force the default comments view, you need to update your hook handlers to return false.
Creating a relationship triggers only one event
Entity relationship creation no longer fires the legacy create event using the relationship name as the type. E.g.
Listening for the "create", "member" event will no longer capture group membership additions. Use the
"create", "relationship" event.
Discussion feature has been pulled from groups into its own plugin
The object, groupforumtopic subtype has been replaced with the object, discussion subtype. If
your plugin is using or altering the old discussion feature, you should upgrade it to use the new subtype.
Nothing changes from the group owners point of view. The discussion feature is still available as a group tool and all
old discussions are intact.
Dropped login-over-https feature
For the best security and performance, serve all pages over HTTPS by switching the scheme in your sites wwwroot
to https at http://yoursite.tld/admin/settings/advanced
149
150
Plugin Messages
Messages will no longer get the metadata msg for newly created messages. This means you can not rely on that
metadata to exist.
Plugin Blog
The blog pages showing Mine or Friends listings of blogs have been changed to list all the blogs owned by the
users (including those created in groups).
Plugin Bookmarks
The bookmark pages showing Mine or Friends listings of bookmarks have been changed to list all the bookmarks
owned by the users (including those created in groups).
Plugin File
The file pages showing Mine or Friends listings of files have been changed to list all the files owned by the users
(including those created in groups).
Removed Classes
ElggInspector
Notable
FilePluginFile: replace with ElggFile (or load with get_entity())
Removed keys available via elgg_get_config()
allowed_ajax_views
dataroot_in_settings
externals
externals_map
i18n_loaded_from_cache
language_paths
pagesetupdone
registered_tag_metadata_names
simplecache_enabled_in_settings
translations
viewpath
views
view_path
viewtype
151
wordblacklist
Also note that plugins should not be accessing the global $CONFIG variable except for in settings.php.
Removed Functions
blog_get_page_content_friends
blog_get_page_content_read
count_unread_messages()
delete_entities()
delete_object_entity()
delete_user_entity()
elgg_get_view_location()
elgg_validate_action_url()
execute_delayed_query()
extend_view()
get_db_error()
get_db_link()
get_entities()
get_entities_from_access_id()
get_entities_from_access_collection()
get_entities_from_annotations()
get_entities_from_metadata()
get_entities_from_metadata_multi()
get_entities_from_relationship()
get_filetype_cloud()
get_library_files()
get_views()
is_ip_in_array()
list_entities()
list_entities_from_annotations()
list_group_search()
list_registered_entities()
list_user_search()
load_plugins()
menu_item()
make_register_object()
mysql_*(): Elgg no longer uses ext/mysql
152
remove_blacklist()
search_for_group()
search_for_object()
search_for_site()
search_for_user()
search_list_objects_by_name()
search_list_groups_by_name()
search_list_users_by_name()
set_template_handler()
test_ip()
Removed methods
ElggCache::set_variable()
ElggCache::get_variable()
ElggData::initialise_attributes()
ElggData::getObjectOwnerGUID()
ElggDiskFilestore::make_directory_root()
ElggDiskFilestore::make_file_matrix()
ElggDiskFilestore::user_file_matrix()
ElggDiskFilestore::mb_str_split()
ElggEntity::clearMetadata()
ElggEntity::clearRelationships()
ElggEntity::clearAnnotations()
ElggEntity::getOwner()
ElggEntity::setContainer()
ElggEntity::getContainer()
ElggEntity::getIcon()
ElggEntity::setIcon()
ElggExtender::getOwner()
ElggFileCache::create_file()
ElggObject::addToSite(): parent function in ElggEntity still available
ElggObject::getSites(): parent function in ElggEntity still available
ElggSite::getCollections()
ElggUser::addToSite(): parent function in ElggEntity still available
ElggUser::getCollections()
ElggUser::getOwner()
153
$limit - 5:
$full_view - 6:
154
155
Language files
In Elgg 1.8 the language files needed to use the add_translation() function. In 1.9 it is enough to just return
the array that was previously passed to the function as a parameter. Elgg core will use the file name (e.g. en.php) to
tell which language the file contains.
156
Notifications
One of the biggest changes in Elgg 1.9 is the notifications system. The new system allows more flexible and scalable
way of sending notifications.
Example of the old way:
function photos_init() {
// Tell core that we want to send notifications about new photos
register_notification_object('object', 'photo', elgg_echo('photo:new'));
// Register a handler that creates the notification message
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'photos_notify_message');
}
/**
* Set the notification message body
*
Hook name
* @param string $hook
Hook type
* @param string $type
@param
string
$message
The current message body
*
* @param array $params Parameters about the photo
* @return string
*/
function photos_notify_message($hook, $type, $message, $params) {
$entity = $params['entity'];
$to_entity = $params['to_entity'];
$method = $params['method'];
if (elgg_instanceof($entity, 'object', 'photo')) {
$descr = $entity->excerpt;
$title = $entity->title;
$owner = $entity->getOwnerEntity();
return elgg_echo('photos:notification', array(
$owner->name,
$title,
$descr,
$entity->getURL()
));
}
return null;
}
157
function photos_init() {
elgg_register_notification_event('object', 'photo', array('create'));
elgg_register_plugin_hook_handler('prepare', 'notification:publish:object:photo', 'photos_prepare
}
/**
* Prepare a notification message about a new photo
*
$hook
Hook name
* @param string
$type
Hook type
* @param string
@param
Elgg_Notifications_Notification
$notification
The notification to prepare
*
$params
Hook parameters
* @param array
* @return Elgg_Notifications_Notification
*/
function photos_prepare_notification($hook, $type, $notification, $params) {
$entity = $params['event']->getObject();
$owner = $params['event']->getActor();
$recipient = $params['recipient'];
$language = $params['language'];
$method = $params['method'];
// Title for the notification
$notification->subject = elgg_echo('photos:notify:subject', array($entity->title), $language);
// Message body for the notification
$notification->body = elgg_echo('photos:notify:body', array(
$owner->name,
$entity->title,
$entity->getExcerpt(),
$entity->getURL()
), $language);
// The summary text is used e.g. by the site_notifications plugin
$notification->summary = elgg_echo('photos:notify:summary', array($entity->title), $language);
return $notification;
}
Note:
See how the community_plugins plugin was updated to use the
https://github.com/Elgg/community_plugins/commit/bfa356cfe8fb99ebbca4109a1b8a1383b70ff123
new
system:
158
159
You can also add the optional target_guid parameter which tells the target of the create action.
If the photo would had been added for example into a photo album, we could add it by passing in also:
'target_guid' => $album_guid,
160
Web services
In Elgg 1.8 the web services API was included in core and methods were exposed using expose_function().
To enable the same functionality for Elgg 1.9, enable the Web services 1.9 plugin and replace all calls to
expose_function() with elgg_ws_expose_function().
Updating plugins
Use standardized routing with page handlers
All: /page_handler/all
3.28. Upgrading Plugins
161
(Example:
bookmarks/all =>
Call set_input() for entity guids in the page handler and use get_input() in the page handler scripts.
Call gatekeeper() and admin_gatekeeper() in the page handler function if required.
The group URL should use the pages/:handler/owner.php script.
Page handlers should not contain HTML.
Update the URLs throughout the plugin. (Dont forget to remove /pg/!)
Use standardized page handlers and scripts
Make sure there are views for $vars[full_view] == true and $vars[full_view] ==
false. $vars[full_view] replaced $vars[full].
Check for the object in $vars[entity]. Use elgg_instance_of() to make sure its the type of
entity you want.
Return true to short circuit the view if the entity is missing or wrong.
Use elgg_view(object/elements/summary, array(entity => $entity)); and
elgg_view_menu(entity, array(entity => $entity)); to help format. You should use
very little markup in these views.
162
mod/blog/actions/blog/save.php =>
Use the blog or file plugins for examples. This will help with making your plugin themeable by the new CSS framework.
Update forms
Move form bodies to the forms/:action view to use Evans new elgg_view_form.
Use input views in form bodies rather than html. This helps with theming and future-proofing.
Add a function that prepares the form (see mod/file/lib/file.php for an example)
Make your forms sticky (see the file plugins upload action and form prepare function).
The forms API is discussed in more detail in Forms + Actions.
Clean up CSS/HTML
We have added many CSS patterns to the base CSS file (modules, image block, spacing primitives). We encourage
you to use these patterns and classes wherever possible. Doing so should:
1. Reduce maintenance costs, since you can delete most custom CSS.
2. Make your plugin more compatible with community themes.
3.28. Upgrading Plugins
163
Look for patterns that can be moved into core if you need significant CSS.
We use hyphens rather than underscores in classes/ids and encourage you do the same for consistency.
If you do need your own CSS, you should use your own namespace, rather than elgg-.
Update manifest.xml
now
plugins/:plugin/usersettings
(previously
System events
User events
Relationship events
Entity events
Metadata events
Annotation events
River events
Notes
regenerate_site_secret:after, system Triggered after the site secret has been regenerated.
log, systemlog Called for all triggered events. Used internally by system_log_default_logger() to populate
the system_log table.
upgrade, system Triggered after a system upgrade has finished. All upgrade scripts have run, but the caches are not
cleared.
upgrade, upgrade
A single upgrade script finished executing. Handlers are passed a stdClass object with the properties
from - The version of Elgg upgrading from.
to - The version just upgraded to.
activate, plugin Return false to prevent activation of the plugin.
deactivate, plugin Return false to prevent deactivation of the plugin.
init:cookie, <name> Return false to override setting a cookie.
cache:flush, system Reset internal and external caches, by default including system_cache, simplecache, and memcache. One might use it to reset others such as APC, OPCache, or WinCache.
165
3.29.8 Notes
Because of bugs in the Elgg core, some events may be thrown more than once on the same action. For example,
update, object is thrown twice.
166
System hooks
User hooks
Object hooks
Action hooks
Permission hooks
Views
Files
Other
Plugins
167
Elgg_Logger::INFO
msg - The message
display - Should this message be displayed?
format, friendly:title Formats the friendly title for strings. This is used for generating URLs.
format, friendly:time Formats the friendly time for the timestamp $params[time].
format, strip_tags Filters a string to
$params[original_string]
$params[allowed_tags].
remove tags.
and an optional
is
is
passed
passed
as
as
output:before, page In elgg_view_page(), this filters $vars before its passed to the page shell
view (page/<page_shell>). To stop sending the X-Frame-Options header, unregister the handler
_elgg_views_send_header_x_frame_options() from this hook.
output, page In elgg_view_page(), this filters the output return value.
output:before, layout In elgg_view_layout(), filters $params before its passed to the layout view.
output:after, layout In elgg_view_layout(), filters the return value of the layout view.
output, ajax Triggered in the ajax forward hook that is called for ajax requests. Allows plugins to alter the output
returned, including the forward URL, system messages, and errors.
parameters, menu:<menu_name> Triggered by elgg_view_menu(). Used to change menu variables (like sort
order) before it is generated.
register, menu:<menu_name> Triggered by elgg_view_menu(). Used to add dynamic menu items.
prepare, menu:<menu_name> Trigger by elgg_view_menu(). Used to sort, add, remove, and modify menu
items.
creating, river Triggered before a river item is created. Return false to prevent river item from being created.
simplecache:generate, <view> Triggered when generating the cached content of a view.
get, subscriptions Filter
notification
subscriptions
$params[event]. Return an array like:
for
users
for
the
Elgg_Notifications_Event
array(
<user guid> => array('subscription', 'types'),
<user_guid2> => array('email', 'sms', 'ajax')
);
prepare, breadcrumbs In elgg_get_breadcrumbs(), this filters the registered breadcrumbs before returning them,
allowing a plugin to alter breadcrumb strategy site-wide.
add, river
168
registeruser:validate:password, all Return boolean for if the string in $params[password] is valid for a
password.
registeruser:validate:email, all Return boolean for if the string in $params[email] is valid for an email address.
register, user Triggered by the register action after the user registers. Return false to delete the user. Note the
function register_user does not trigger this hook.
login:forward, user Filters the URL to which the user will be forwarded after login.
find_active_users, system Return the number of active users.
status, user Triggered by The Wire when adding a post.
username:character_blacklist, user Filters the string of blacklisted characters used to validate username during
registration. The return value should be a string consisting of the disallowed characters. The default string can
be found from $params[blacklist].
169
permissions_check:comment, <entity_type> Return boolean for if the user $params[user] can comment on
the entity $params[entity].
permissions_check:annotate Return boolean for if the user $params[user] can create an annotation with the
name $params[annotation_name] on the entity $params[entity].
Warning: This is functions differently than the permissions_check:metadata hook by passing the
annotation name instead of the metadata object.
permissions_check:annotation Return boolean for if the user in $params[user] can edit the annotation
$params[annotation] on the entity $params[entity]. The user can be null.
fail, auth Return the failure message if authentication failed. An array of previous PAM failure methods is passed as
$params.
api_key, use Triggered by api_auth_key(). Returning false prevents the key from being authenticated.
access:collections:read, user Filters an array of access IDs that the user $params[user_id] can see.
Warning: The handler needs to either not use parts of the API that use the access system (triggering the
hook again) or to ignore the second call. Otherwise, an infinite loop will be created.
access:collections:write, user Filters an array of access IDs that the user $params[user_id] can write to.
In get_write_access_array(), this hook filters the return value, so it can be used to alter the available options
in the input/access view. For core plugins, the value input_params has the keys entity (ElggEntity|false),
entity_type (string), entity_subtype (string), container_guid (int) are provided. An empty entity value
generally means the form is to create a new object.
Warning: The handler needs to either not use parts of the API that use the access system (triggering the
hook again) or to ignore the second call. Otherwise, an infinite loop will be created.
access:collections:addcollection, collection Triggered after an access collection $params[collection_id]
is created.
access:collections:deletecollection, collection Triggered
before
an
$params[collection_id] is deleted. Return false to prevent deletion.
access
collection
3.30.6 Views
view_vars, <view_name> Filters the $vars array passed to the view
view, <view_name> Filters the returned content of the view
layout, page In elgg_view_layout(), filters the layout name
shell, page In elgg_view_page(), filters the page shell name
head, page In elgg_view_page(), filters $vars[head]
170
3.30.7 Files
mime_type, file Return the mimetype for the filename $params[filename] with original filename $params[original_filename] and with the default detected mimetype of
$params[default].
simple_type, file In elgg_get_file_simple_type(), filters the return value.
The hook uses
$params[mime_type] (e.g. application/pdf or image/jpeg) and determines an overall
category like document or image. The bundled file plugin and other-third party plugins usually store
simpletype metadata on file entities and make use of it when serving icons and constructing ege* filters
and menus.
3.30.8 Other
config, comments_per_page Filters the number of comments displayed per page. Default is 25.
default, access In get_default_access(), this hook filters the return value, so it can be used to alter the default value
in the input/access view. For core plugins, the value input_params has the keys entity (ElggEntity|false),
entity_type (string), entity_subtype (string), container_guid (int) are provided. An empty entity value
generally means the form is to create a new object.
entity:icon:url, <entity_type> Triggered when entity icon URL is requested, see entity icons. Callback should return
URL for the icon of size $params[size] for the entity $params[entity]. Following parameters
are available through the $params array:
entity Entity for which icon url is requested.
viewtype The type of view e.g. default or json.
size Size requested, see entity icons for possible values.
Example on how one could default to a Gravatar icon for users that have not yet uploaded an avatar:
// Priority 600 so that handler is triggered after avatar handler
elgg_register_plugin_hook_handler('entity:icon:url', 'user', 'gravatar_icon_handler', 600);
/**
* Default to icon from gravatar for users without avatar.
*/
function gravatar_icon_handler($hook, $type, $url, $params) {
// Allow users to upload avatars
if ($params['entity']->icontime) {
return $url;
}
// Generate gravatar hash for user email
$hash = md5(strtolower(trim($params['entity']->email)));
// Default icon size
$size = '150x150';
// Use configured size if possible
$config = elgg_get_config('icon_sizes');
$key = $params['size'];
if (isset($config[$key])) {
$size = $config[$key]['w'] . 'x' . $config[$key]['h'];
}
// Produce URL used to retrieve icon
171
return "http://www.gravatar.com/avatar/$hash?s=$size";
}
entity:url, <entity_type> Return the URL for the entity $params[entity]. Note: Generally it is better to
override the getUrl() method of ElggEntity. This hook should be used when its not possible to subclass
(like if you want to extend a bundled plugin without overriding many views).
to:object, <entity_type|metadata|annotation|relationship|river_item> Converts
the
entity
$params[entity] to a StdClass object. This is used mostly for exporting entity properties for
portable data formats like JSON and XML.
extender:url, <annotation|metadata> Return
$params[extender].
the
URL
for
the
annotation
or
metadatum
the
URL
for
the
relationship
object
profile:fields, group Filter an array of profile fields. The result should be returned as an array in the format name
=> input view name. For example:
array(
'about' => 'longtext'
);
profile:fields, profile Filter an array of profile fields. The result should be returned as an array in the format name
=> input view name. For example:
array(
'about' => 'longtext'
);
widget_settings, <widget_handler> Triggered when saving a widget settings $params[params] for widget
$params[widget]. If handling saving the settings, the handler should return true to prevent the default
code from running.
172
get_list, default_widgets Filters a list of default widgets to add for newly registered users. The list is an array of
arrays in the format:
array(
'event' => $event,
'entity_type' => $entity_type,
'entity_subtype' => $entity_subtype,
'widget_context' => $widget_context
)
rest, init Triggered by the web services rest handler. Plugins can set up their own authentication handlers, then return
true to prevent the default handlers from being registered.
public_pages, walled_garden Filter the URLs that are can be seen by logged out users if Walled Garden is enabled.
$value is an array of regex strings that will allow access if matched.
volatile, metadata Triggered when exporting an entity through the export handler. This is rare. This allows handler
to handle any volatile (non-persisted) metadata on the entity. Its preferred to use the to:object, <type>
hook.
maintenance:allow, url
Return boolean if the URL $params[current_url] and the path $params[current_path]
is allowed during maintenance mode.
robots.txt, site Filter the robots.txt values for $params[site].
config, amd Filter the AMD config for the requirejs library.
3.30.9 Plugins
Embed
embed_get_items, <active_section>
embed_get_sections, all
embed_get_upload_sections, all
HTMLawed
allowed_styles, htmlawed Filter the HTMLawed allowed style array.
config, htmlawed Filter the HTMLawed config array.
Members
members:list, <page_segment> To handle the page /members/$page_segment, register for this hook and return the HTML of the list.
members:config, tabs This hook is used to assemble an array of tabs to be passed to the navigation/tabs view for the
members pages.
Twitter API
authorize, twitter_api Triggered when a user is authorizes Twitter for a login. $params[token] contains the
Twitter authorization token.
3.30. List of plugin hooks in core
173
Reported Content
reportedcontent:add, system Triggered after adding the reported content object $params[report]. Return
false to delete report.
reportedcontent:archive, system Triggered before archiving the reported content object $params[report].
Return false to prevent archiving.
reportedcontent:delete, system Triggered before deleting the reported content object $params[report]. Return false to prevent deleting.
Search
search, <type>:<subtype> Filter more granular search results than searching by type alone. Must return an array
with count as the total count of results and entities an array of ElggUser entities.
search, tags
search, <type> Filter the search for entities for type $type. Must return an array with count as the total count of
results and entities an array of ElggUser entities.
search_types, get_types Filter an array of search types. This allows plugins to add custom types that dont correspond
directly to entities.
search_types, get_queries Before a search this filters the types queried. This can be used to reorder the display of
search results.
174
CHAPTER 4
Tutorials
This piece of code tells Elgg that it should call the function hello_world_init() once the Elgg core system is
initiated.
175
}
function hello_world_page_handler() {
echo elgg_view_resource('hello');
}
The call to elgg_register_page_handler() tells Elgg that it should call the function
hello_world_page_handler() when user goes navigates to https://elgg.example.com/hello/*.
The hello_world_page_handler() passes off rendering the actual page to the resources/hello view.
Create views/default/resources/hello.php with this content:
<?php
$params = array(
'title' => 'Hello world!',
'content' => 'My first page!',
'filter' => '',
);
$body = elgg_view_layout('content', $params);
echo elgg_view_page('Hello', $body);
176
Chapter 4. Tutorials
Contents
Prerequisites:
Install Elgg
177
178
Chapter 4. Tutorials
A few fields are built into Elgg objects. Title and description are two of these. It makes sense to use description to
contain the my_blog text. Every entity can have a subtype and in this we are using "my_blog". The tags are stored
as metadata.
Every object in Elgg has a built-in URL automatically, although you can override this if you wish. The getURL()
method is called to get that unique URL.
The last line takes the tags on the my_blog post and automatically displays them as a series of clickable links. Search
is handled automatically.
(If youre wondering about the default in /views/default/, you can create alternative views. RSS, OpenDD,
FOAF, mobile and others are all valid view types.)
4.3.5 start.php
For this example, we just need to register the action file we created earlier: Also see a related guide about Forms +
Actions.
<?php
elgg_register_action("my_blog/save", __DIR__ . "/actions/my_blog/save.php");
The action will now be available as /action/my_blog/save. By default, all actions are available only to logged
in users. If you want to make an action available to only admins or open it up to unauthenticated users, you can pass
admin or public as the third parameter of elgg_register_action(), respectively.
179
Page handling functions need to return true or false. true means the page exists and has been handled by
the page handler. false means that the page does not exist and the user will be forwarded to the sites 404 page
(requested page does not exist or not found). In this particular example, the URL must contain /my_blog/add for
the user to view a page with a form, otherwise the user will see a 404 page.
The function elgg_list_entities (and its cousins) also transparently handle pagination, and even create an RSS feeds
for your my_blogs if you have defined these views.
Finally, well draw the page:
$body = elgg_view_layout('one_column', array('content' => $body));
echo elgg_view_page("All Site Blogs", $body);
We will then need to modify our my_blog page handler to grab the new page when the URL is set to /my_blog/all.
So, your new my_blog_page_handler() function in start.php should look like:
function my_blog_page_handler($segments) {
switch ($segments[0]) {
case 'add':
echo elgg_view_resource('my_blog/add');
break;
case 'all':
180
Chapter 4. Tutorials
default:
echo elgg_view_resource('my_blog/all');
break;
}
return true;
}
Now, if the URL contains just /my_blog or /my_blog/all, the user will see an All Site Blogs page.
181
Create a view
<?php
/**
* Elgg long text input with the tinymce text editor intacts
* Displays a long text input field
*
* @package ElggTinyMCE
*
*
*/
?>
<!-- include tinymce -->
<script language="javascript" type="text/javascript" src="<?php echo $vars['url']; ?>mod/tinymce/tiny
<!-- intialise tinymce, you can find other configurations here http://wiki.moxiecode.com/examples/tin
<script language="javascript" type="text/javascript">
tinyMCE.init({
mode : "textareas",
theme : "advanced",
theme_advanced_buttons1 : "bold,italic,underline,separator,strikethrough,justifyleft,justifycente
theme_advanced_buttons2 : "",
theme_advanced_buttons3 : "",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
extended_valid_elements : "a[name|href|target|title|onclick],img[class|src|border=0|alt|title|hsp
hr[class|width|size|noshade],font[face|size|color|style],span[class|align|style]"
});
</script>
Thats it! Now every time someone uses input/longtext, TinyMCE will be loaded and applied to that textarea.
182
Chapter 4. Tutorials
Contents
Adding the widget view code
Registering your widget
Allow user customization
This will add these words to the widget canvas when it is drawn. Elgg takes care of loading the widget.
Now go to your profile page using a web browser and add the hello, world widget. It should display Hello, world!.
Note: For real widgets, it is always a good idea to support Internationalization.
183
Notice the relationship between the values passed to the name and the value fields of input/text. The name of
the input text box is params[message] because Elgg will automatically handle widget variables put in the array
params. The actual php variable name will be message. If we wanted to use the field greeting instead of
message we would pass the values params[greeting] and $widget->greeting respectively.
The reason we set the value option of the array is so that the edit view remembers what the user typed in the previous
time he changed the value of his message text.
Now to display the users message we need to modify content.php to use this message variable.
/views/default/widgets/helloworld/content.php and change it to:
Edit
<?php
$widget = $vars['entity'];
// Always use the corresponding output/* view for security!
echo elgg_view('output/text', array('value' => $widget->message));
You should now be able to enter a message in the text box and see it appear in the widget.
184
Chapter 4. Tutorials
CHAPTER 5
Design Docs
Gain a deep understanding of how Elgg works and why its built the way it is.
5.1 Actions
Actions are the primary way users interact with an Elgg site.
5.1.1 Overview
An action in Elgg is the code that runs to make changes to the database when a user does something. For example,
logging in, posting a comment, and making a blog post are actions. The action script processes input, makes the
appropriate modifications to the database, and provides feedback to the user about the action.
5.2 Database
A thorough discussion of Elggs data model design and motivation.
185
Contents
Overview
Datamodel
Entities
Users, Objects, Groups, Sites
GUIDs
ElggObject
ElggUser
ElggSite
ElggGroup
The Groups plugin
Writing a group-aware plugin
Ownership
Containers
Annotations
Adding an annotation
Reading annotations
Useful helper functions
Metadata
The simple case
Finer control
Common mistakes
Relationships
Working with relationships
Access Control
Access controls in the data model
How access affects data retrieval
Write access
Schema
Main tables
5.2.1 Overview
In Elgg, everything runs on a unified data model based on atomic units of data called entities.
Plugins are discouraged from interacting directly with the database, which creates a more stable system and a better
user experience becase content created by different plugins can be mixed together in consistent ways. With this
approach, plugins are faster to develop, and are at the same time much more powerful.
Every entity in the system inherits the ElggEntity class. This class controls access permissions, ownership
You can extend entities with extra information in two ways:
Metadata: This is information describing the entity, usually added by the author of the entity when the entity is
created. For example, tags, an ISBN number, a file location, or source language is metadata.
Annotations: This is information about the entity, usually added by a third party after the entity is created. For
example, ratings, likes, and votes are annotations. (Comments were before 1.9.)
186
5.2. Database
187
5.2.2 Datamodel
5.2.3 Entities
ElggEntity is the base class for the Elgg data model.
Users, Objects, Groups, Sites
ElggEntity has four main specializations, which provide extra properties and methods to more easily handle different kinds of data.
ElggObject: content like blog posts, uploaded files and bookmarks ElggUser: a system user ElggSite: each
Elgg site within an Elgg installation ElggGroup: multi-user collaborative systems (called Communities in prior
versions of Elgg)
The benefit of such an approach is that, apart from modelling data with greater ease, a common set of functions is
available to handle objects, regardless of their (sub)type.
Each of these have their own properties that they bring to the table: ElggObjects have a title and description, ElggUsers
have a username and password, and so on. However, because they all inherit ElggEntity, they each have a number of
core properties and behaviours in common.
A numeric Globally Unique IDentifier (See GUIDs).
Access permissions. (When a plugin requests data, it never gets to touch data that the current user doesnt have
permission to see.)
An arbitrary subtype. For example, a blog post is an ElggObject with a subtype of blog. Subtypes arent
predefined; they can be any unique way to describe a particular kind of entity. blog, forum, foo, bar,
loafofbread and pyjamas are all valid subtypes.
An owner.
The site that the entity belongs to.
A container, usually used to associate a groups content with the group.
GUIDs
A GUID is an integer that uniquely identifies every entity in an Elgg installation (a Globally Unique IDentifier). Its
assigned automatically when the entity is first saved and can never be changed.
Some Elgg API functions work with GUIDs instead of ElggEntity objects.
5.2.4 ElggObject
The ElggObject entity type represents arbitrary content within an Elgg install; things like blog posts, uploaded
files, etc.
Beyond the standard ElggEntity properties, ElggObjects also support:
title The title of the object (HTML escaped text)
description A description of the object (HTML)
Most other data about the object is generally stored via metadata.
188
5.2.5 ElggUser
The ElggUser entity type represents users within an Elgg install. These will be set to disabled until their accounts
have been activated (unless they were created from within the admin panel).
Beyond the standard ElggEntity properties, ElggUsers also support:
name The users plain text name. e.g. Hugh Jackman
username Their login name. E.g. hjackman
password A hashed version of their password
salt The salt that their password has been hashed with
email Their email address
language Their default language code.
code Their session code (moved to a separate table in 1.9).
last_action The UNIX timestamp of the last time they loaded a page
prev_last_action The previous value of last_action
last_login The UNIX timestamp of their last log in
prev_last_login the previous value of last_login
5.2.6 ElggSite
The ElggSite entity type represents sites within your Elgg install. Most installs will have only one.
Beyond the standard ElggEntity properties, ElggSites also support:
name The site name
description A description of the site
url The address of the site
5.2.7 ElggGroup
The ElggGroup entity type represents an association of Elgg users. Users can join, leave, and post content to groups.
Beyond the standard ElggEntity properties, ElggGroups also support:
name The groups name (HTML escaped text)
description A description of the group (HTML)
ElggGroup has addition methods to manage content and membership.
The Groups plugin
Not to be confused with the entity type ElggGroup, Elgg comes with a plugin called Groups that provides a default
UI/UX for site users to interact with groups. Each group is given a discussion forum and a profile page linking users
to content within the group.
You can alter the user experience via the traditional means of extending plugins or completely replace the Groups
plugin with your own.
5.2. Database
189
Because ElggGroup can be subtyped like all other ElggEntities, you can have multiple types of groups running on
the same site.
Writing a group-aware plugin
Plugin owners need not worry too much about writing group-aware functionality, but there are a few key points:
Adding content
By passing along the group as container_guid via a hidden input field, you can use a single form and action to
add both user and group content.
Use can_write_to_container to determine whether or not the current user has the right to add content to a group.
Be aware that you will then need to pass the container GUID or username to the page responsible for posting and
the accompanying value, so that this can then be stored in your form as a hidden input field, for easy passing to your
actions. Within a create action, youll need to take in this input field and save it as a property of your new element
(defaulting to the current users container):
$user = elgg_get_logged_in_user_entity();
$container_guid = (int)get_input('container_guid');
if ($container_guid) {
if (!can_write_to_container($user->guid, $container_guid)) {
// register error and forward
}
} else {
$container_guid = elgg_get_logged_in_user_guid();
}
$object = new ElggObject;
$object->container_guid = $container_guid;
...
$container = get_entity($container_guid);
forward($container->getURL());
Groups have a simulated username of the form group:GUID, which you can get the value of by checking
$group->username. If you pass this username to a page on the URL line as part of the username variable
(i.e., /yourpage?username=group:nnn), Elgg will automatically register that group as being the owner of the
page (unless overridden).
Juggling users and groups
190
Menu options
You can then extend the groups menu view with this one, within your plugins input function (in this case file_init):
extend_view('groups/menu/links', 'file/menu');
5.2.8 Ownership
Entities have a owner_guid GUID property, which defines its owner. Typically this refers to the GUID of a user,
although sites and users themselves often have no owner (a value of 0).
The ownership of an entity dictates, in part, whether or not you can access or edit that entity.
5.2.9 Containers
In order to easily search content by group or by user, content is generally set to be contained by either the user who
posted it, or the group to which the user posted. This means the new objects container_guid property will be set
to the GUID of the current ElggUser or the target ElggGroup.
E.g., three blog posts may be owned by different authors, but all be contained by the group they were posted to.
Note: This is not always true. Comment entities are contained by the object commented upon, and in some 3rd
party plugins the container may be used to model a parent-child relationship between entities (e.g. a folder object
containing a file object).
5.2.10 Annotations
Annotations are pieces of data attached to an entity that allow users to leave ratings, or other relevant feedback. A poll
plugin might register votes as annotations. Before Elgg 1.9, comments and group discussion replies were stored as
annotations.
Annotations are stored as instances of the ElggAnnotation class.
Each annotation has:
An internal annotation type (like comment)
A value (which can be a string or integer)
An access permission distinct from the entity its attached to
An owner
5.2. Database
191
Adding an annotation
The easiest way to annotate is to use the annotate method on an entity, which is defined as:
public function annotate(
$name,
// The name of the annotation type (eg 'comment')
$value,
// The value of the annotation
$access_id = 0, // The access level of the annotation
$owner_id = 0,
// The annotation owner, defaults to current user
$vartype = ""
// 'text' or 'integer'
)
Reading annotations
To retrieve annotations on an object, you can call the following method:
$annotations = $entity->getAnnotations(
$name,
// The type of annotation
$limit,
// The number to return
$offset, // Any indexing offset
$order,
// 'asc' or 'desc' (default 'asc')
);
If your annotation type largely deals with integer values, a couple of useful mathematical functions are provided:
$averagevalue = $entity->getAnnotationsAvg($name);
$total = $entity->getAnnotationsSum($name);
$minvalue = $entity->getAnnotationsMin($name);
$maxvalue = $entity->getAnnotationsMax($name);
//
//
//
//
Get
Get
Get
Get
the
the
the
the
average value
total value
minimum value
maximum value
If you want to provide comment functionality on your plugin objects, the following function will provide the full
listing, form and actions:
function elgg_view_comments(ElggEntity $entity)
5.2.11 Metadata
Metadata in Elgg allows you to store extra data on an entity beyond the built-in fields that entity supports. For
example, ElggObjects only support the basic entity fields plus title and description, but you might want to include
tags or an ISBN number. Similarly, you might want users to be able to save a date of birth.
Under the hood, metadata is stored as an instance of the ElggMetadata class, but you dont need to worry about
that in practice (although if youre interested, see the ElggMetadata class reference). What you need to know is:
Metadata has an owner and access ID, both of which may be different to the owner of the entity its attached to
You can potentially have multiple items of each type of metadata attached to a single entity
192
Note that this will return the absolute value of the metadata. To get metadata as an ElggMetadata object, you will need
to use the methods described in the finer control section below.
If you stored multiple values in this piece of metadata (as in the tags example above), you will get an array of all
those values back. If you stored only one value, you will get a string or integer back. Storing an array with only one
value will return a string back to you. E.g.
$object->tags = array('tag');
$tags = $object->tags;
// $tags will be the string "tag", NOT array('tag')
Finer control
Adding metadata
If you need more control, for example to assign an access ID other than the default, you can use the
create_metadata function, which is defined as follows:
5.2. Database
193
function create_metadata(
$entity_guid,
$name,
$value,
$value_type,
$owner_guid,
$access_id = 0,
$allow_multiple = false
)
//
//
//
//
//
//
//
For single values, you can therefore write metadata as follows (taking the example of a date of birth attached to a user):
create_metadata($user_guid, 'dob', $dob_timestamp, 'integer', $_SESSION['guid'], $access_id);
For multiple values, you will need to iterate through and call create_metadata on each one. The following piece
of code comes from the profile save action:
$i = 0;
foreach ($value as $interval) {
$i++;
$multiple = ($i != 1);
create_metadata($user->guid, $shortname, $interval, 'text', $user->guid, $access_id, $multiple);
}
Note that the allow multiple setting is set to false in the first iteration and true thereafter.
Reading metadata
Common mistakes
Appending metadata
Note that you cannot append values to metadata arrays as if they were normal php arrays. For example, the following
will not do what it looks like it should do.
$object->tags[] = "tag four";
194
Elgg does not support storing ordered maps (name/value pairs) in metadata. For example, the following does not work
as you might first expect it to:
// Won't work!! Only the array values are stored
$object->tags = array('one' => 'a', 'two' => 'b', 'three' => 'c');
Though there are some cases to store entity GUIDs in metadata, Relationships are a much better construct for relating
entities to each other.
5.2.12 Relationships
Relationships allow you to bind entities together. Examples: an artist has fans, a user is a member of an organization,
etc.
The class ElggRelationship models a directed relationship between two entities, making the statement:
{subject} is a {noun} of {target}.
API name
guid_one
relationship
guid_two
Models
The subject
The noun
The target
Represents
Which entity is being bound
The type of relationship
The entity to which the subject is bound
E.g. to establish that $user is a fan of $artist (user is the subject, artist is the target):
// option 1
$success = add_entity_relationship($user->guid, 'fan', $artist->guid);
5.2. Database
195
// option 2
$success = $user->addRelationship($artist->guid, 'fan');
This triggers the event [create, relationship], passing in the created ElggRelationship object. If a handler returns
false, the relationship will not be created and $success will be false.
Verifying a relationship
Deleting a relationship
This triggers the event [delete, relationship], passing in the associated ElggRelationship object. If a handler
returns false, the relationship will remain, and $was_removed will be false.
Other useful functions:
delete_relationship() : delete by ID
remove_entity_relationships() : delete those relating to an entity (note: in versions before Elgg
1.9, this did not trigger delete events)
Finding relationships and related entities
Below are a few functions to fetch relationship objects and/or related entities. A few are listed below:
get_entity_relationships() : fetch relationships by subject or target entity
get_relationship() : get a relationship object by ID
elgg_get_entities_from_relationship() : fetch entities in relationships in a variety of ways
E.g. retrieving users who joined your site in January 2014.
$entities = elgg_get_entities_from_relationship(array(
'relationship' => 'member_of_site',
'relationship_guid' => elgg_get_site_entity()->guid,
'inverse_relationship' => true,
'relationship_created_time_lower' => 1388534400, // January 1st 2014
'relationship_created_time_upper' => 1391212800, // February 1st 2014
));
196
You may define additional access groups and assign them to an entity, annotation or metadata. A number of functions
have been defined to assist you; see the access library reference for more information.
How access affects data retrieval
All data retrieval functions above the database layer - for example get_entities and its cousins - will only return
items that the current user has access to see. It is not possible to retrieve items that the current user does not have
access to. This makes it very hard to create a security hole for retrieval.
Write access
The following rules govern write access:
The owner of an entity can always edit it
The owner of a container can edit anything therein (note that this does not mean that the owner of a group can
edit anything therein)
Admins can edit anything
You can override this behaviour using a plugin hook called permissions_check, which passes the entity in
question to any function that has announced it wants to be referenced. Returning true will allow write access;
returning false will deny it. See the plugin hook reference for permissions_check for more details.
See also:
Access library reference
5.2. Database
197
5.2.14 Schema
The database contains a number of primary tables and secondary tables.
/engine/schema/mysql.sql.
Each table is prefixed by prefix_, this is replaced by the Elgg framework during installation.
Main tables
This is a description of the main tables. Keep in mind that in a given Elgg installation, the tables will have a prefix
(typically elgg_).
Table: entities
This is the main Entities table containing Elgg users, sites, objects and groups. When you first install Elgg this is
automatically populated with your first site.
It contains the following fields:
guid An auto-incrementing counter producing a GUID that uniquely identifies this entity in the system.
type The type of entity - object, user, group or site
subtype A link to the entity_subtypes table.
owner_guid The GUID of the owners entity.
site_guid The site the entity belongs to.
container_guid The GUID this entity is contained by - either a user or a group.
access_id Access controls on this entity.
time_created Unix timestamp of when the entity is created.
time_updated Unix timestamp of when the entity was updated.
enabled If this is yes an entity is accessible, if no the entity has been disabled (Elgg treats it as if it were
deleted without actually removing it from the database).
Table: entity_subtypes
198
This table defines Relationships, these link one entity with another.
guid_one The GUID of the subject entity.
relationship The type of the relationship.
guid_two The GUID of the target entity.
Table: objects_entity
Extra information specifically relating to objects. These are split in order to reduce load on the metadata table and
make an obvious difference between attributes and metadata.
Table: sites_entity
Extra information specifically relating to sites. These are split in order to reduce load on the metadata table and make
an obvious difference between attributes and metadata.
5.2. Database
199
Table: users_entity
Extra information specifically relating to users. These are split in order to reduce load on the metadata table and make
an obvious difference between attributes and metadata.
Table: groups_entity
Extra information specifically relating to groups. These are split in order to reduce load on the metadata table and
make an obvious difference between attributes and metadata.
Table: metastrings
Metastrings contain the actual string of metadata which is linked to by the metadata and annotations tables.
This is to avoid duplicating strings, saving space and making database lookups more efficient.
5.3.1 Overview
Elgg has an event system that can be used to replace or extend core functionality.
Plugins influence the system by creating handlers (callables such as functions and methods) and registering them to
handle two types of events: Elgg Events and Plugin Hooks.
When an event is triggered, a set of handlers is executed in order of priority. Each handler is passed arguments and
has a chance to influence the process. After execution, the trigger function returns a value based on the behavior of
the handlers.
Elgg Events vs. Plugin Hooks
The main differences between Elgg Events and Plugin Hooks are:
200
1. Most Elgg events can be cancelled; unless the event is an after event, a handler that returns false can cancel
the event, and no more handlers are called.
2. Plugin hooks cannot be cancelled; all handlers are always called.
3. Plugin hooks pass an arbitrary value through the handlers, giving each a chance to alter along the way.
If the handler returns false, the event is cancelled, preventing execution of the other handlers. All other return values
are ignored.
Register to handle an Elgg Event
Register your handler to an event using elgg_register_event_handler:
elgg_register_event_handler($event, $object_type, $handler, $priority);
201
Parameters:
$event The event name.
$object_type The object type (e.g. user or object) or all for all types on which the event is fired.
$handler The callback of the handler function.
$priority The priority - 0 is first and the default is 500.
Object here does not refer to an ElggObject but rather a string describing any object in the framework: system,
user, object, relationship, annotation, group.
Example:
// Register the function myPlugin_handle_login() to handle the
// user login event with priority 400.
elgg_register_event_handler('login', 'user', 'myPlugin_handle_login', 400);
For events with ambiguous states, like logging in a user, you should use Before and After Events by calling
elgg_trigger_before_event or elgg_trigger_after_event. This makes it clear for the event handler
what state to expect and which events can be cancelled.
// handlers for the user, login:before event know the user isn't logged in yet.
if (!elgg_trigger_before_event('login', 'user', $user)) {
return false;
}
// handlers for the user, login:after event know the user is logged in.
elgg_trigger_after_event('login', 'user', $user);
Parameters:
$event The event name.
$object_type The object type (e.g. user or object).
$object The object (e.g. an instance of ElggUser or ElggGroup)
The function will return false if any of the selected handlers returned false and the event is stoppable, otherwise
it will return true.
202
If the handler returns no value (or null explicitly), the plugin hook value is not altered. Otherwise the return value
becomes the new value of the plugin hook. It will then be passed to the next handler as $value.
Register to handle a Plugin Hook
Register your handler to a plugin hook using elgg_register_plugin_hook_handler:
elgg_register_plugin_hook_handler($hook, $type, $handler, $priority);
Parameters:
$hook The name of the plugin hook.
$type The type of the hook or all for all types.
$handler The callback of the handler function.
$priority The priority - 0 is first and the default is 500.
Type can vary in meaning. It may mean an Elgg entity type or something specific to the plugin hook name.
Example:
// Register the function myPlugin_hourly_job() to be called with priority 400.
elgg_register_plugin_hook_handler('cron', 'hourly', 'myPlugin_hourly_job', 400);
Parameters:
$hook The name of the plugin hook.
$type The type of the hook or all for all types.
$params Arbitrary data passed from the trigger to the handlers.
$value The initial value of the plugin hook.
203
Warning: The $params and $value arguments are reversed between the plugin hook handlers and trigger functions!
Anonymous functions or invokable objects cannot be unregistered, but dynamic method callbacks can be unregistered
by giving the static version of the callback:
$obj = new MyPlugin\Handlers();
elgg_register_plugin_hook_handler('foo', 'bar', [$obj, 'handleFoo']);
// ... elsewhere
elgg_unregister_plugin_hook_handler('foo', 'bar', 'MyPlugin\Handlers::handleFoo');
Even though the event handler references a dynamic method call, the code above will successfully remove the handler.
Handler Calling Order
Handlers are called first in order of priority, then registration order.
Note: Before Elgg 2.0, registering with the all keywords caused handlers to be called later, even if they were
registered with lower priorities.
5.4 Internationalization
Elgg 1.0+ departs from previous versions in that it uses a custom text array rather than gettext. This improves system
performance and reliability of the translation system.
TODO: more plz
5.5 AMD
5.5.1 Overview
There are two JavaScript system in Elgg: the deprecated 1.8 system, and the newer AMD (Asynchronous Module
Definition) compatible system introduced in 1.9.
This discusses the benefits of using AMD in Elgg.
204
5.6 Security
Elggs approach to the various security issues common to all web applications.
Tip: To report a potential vulnerability in Elgg, email [email protected].
1 This is not currently supported by Elgg core, but well be looking into it since reducing round-trips is critical for a good first-view experience,
especially on mobile devices.
5.6. Security
205
Contents
Passwords
Password validation
Password salting
Password hashing
Password storage
Password throttling
Password resetting
Sessions
Session fixation
Session hijacking
Remember me cookie
Alternative authentication
HTTPS
XSS
CSRF / XSRF
SQL Injection
Privacy
5.6.1 Passwords
Password validation
The only restriction that Elgg places on a password is that it must be at least 6 characters long by default, though
this may be changed in /engine/settings.php. Additional criteria can be added by a plugin by registering for the
registeruser:validate:password plugin hook.
Password salting
Elgg salts passwords with a unique 8 character random string. The salt is generated each time the password is set. The main sec
preventing anyone with access to the database from conducting a precomputed dictionary attack
preventing a site administration from noting users with the same password.
Password hashing
The hashed password is computed using md5 from the users password text and the salt.
Password storage
The hashed password and the salt are stored in the users table. Neither are stored in any cookies on a users computer.
Password throttling
Elgg has a password throttling mechanism to make dictionary attacks from the outside very difficult. A user is only
allowed 5 login attempts over a 5 minute period.
206
Password resetting
If a user forgets his password, a new random password can be requested. After the request, an email is sent with a
unique URL. When the user visits that URL, a new random password is sent to the user through email.
5.6.2 Sessions
Elgg uses PHPs session handling with custom handlers. Session data is stored in the database. The session cookie
contains the session id that links the user to the browser. The users metadata is stored in the session including GUID,
username, email address. The sessions lifetime is controlled through the servers PHP configuration.
Session fixation
Elgg protects against session fixation by regenerating the session id when a user logs in.
Session hijacking
Warning: This section is questionable.
Besides protecting against session fixation attacks, Elgg also has a further check to try to defeat session hijacking if
the session identifier is compromised. Elgg stores a hash of the browsers user agent and a site secret as a session
fingerprint. The use of the site secret is rather superfluous but checking the user agent might prevent some session
hijacking attempts.
Remember me cookie
To allow users to stay logged in for a longer period of time regardless of whether the browser has been closed, Elgg
uses a cookie (called elggperm) that contains what could be considered a super session identifier. This identifier is
stored in a cookies table. When a session is being initiated, Elgg checks for the presence of the elggperm cookie. If it
exists and the session code in the cookie matches the code in the cookies table, the corresponding user is automatically
logged in.
5.6.4 HTTPS
Note: You must enable SSL support on your server for any of these techniques to work.
5.6. Security
207
To make the login form submit over https, turn on login-over-ssl from Elggs admin panel.
You can also serve your whole site over SSL by simply changing the site URL to include https instead of just http.
5.6.5 XSS
Filtering is used in Elgg to make XSS attacks more difficult. The purpose of the filtering is to remove Javascript and
other dangerous input from users.
Filtering is performed through the function filter_tags(). This function takes in a string and returns a filtered
string. It triggers a validate, input plugin hook.
By default Elgg comes with the htmLawed filtering code as a plugin. Developers can drop in any additional or
replacement filtering code as a plugin.
The filter_tags() function is called on any user input as long as the input is obtained through a call to
get_input(). If for some reason a developer did not want to perform the default filtering on some user input,
the get_input() function has a parameter for turning off filtering.
5.6.8 Privacy
Elgg uses an ACL system to control which users have access to various pieces of content. Read more in the Database
design doc.
5.7 Loggable
Loggable is an interface inherited by any class that wants events relating to its member objects to be saved to the
system log. ElggEntity and ElggExtender both inherit Loggable.
Loggable defines several class methods that are used in saving to the default system log, and can be used to define
your own (as well as for other purposes):
getSystemLogID() Return a unique identifier for the object for storage in the system log. This is likely to
be the objects GUID
getClassName() Return the class name of the object
getType() Return the object type
getSubtype() Get the object subtype
getObjectFromID($id) For a given ID, return the object associated with it
208
5.7. Loggable
209
210
CHAPTER 6
Contributor Guides
6.1 Translations
Translations multiply the impact that Elgg can have by making it accessible to a larger percentage of the world.
The community will always be indebted to those of you who work hard to provide high quality translations for Elggs
UI and docs.
6.1.1 Transifex
All translation for the Elgg project is organized through Transifex.
https://www.transifex.com/organization/elgg
Plugin authors are encouraged to coordinate translations via Transifex as well so the whole community can be unified
and make it really easy for translators to contribute to any plugin in the Elgg ecosystem.
6.2.1 DISCLAIMERS
SECURITY ISSUES SHOULD BE REPORTED TO security @ elgg . org! Please do not post any security
issues on github!!
Support requests belong on the community site. Tickets with support requests will be closed.
We cannot make any guarantees as to when your ticket will be resolved.
211
License agreement
Pull requests
Testing
Coding best practices
Deprecating APIs
212
[
[
[
[
[
]
]
]
]
]
Feature PRs:
-
[
[
[
[
[
]
]
]
]
]
The difference between minor and major feature is subjective and up to the core team.
Commit message format
We require a particular format to allow releasing more often, and with improved changelogs and source history. Just
follow these steps:
1. Start with the type by selecting the last category which applies from this list:
docs - only docs are being updated
chore - this include refactoring, code style changes, adding missing tests, Travis stuff, etc.
perf - the primary purpose is to improve performance
fix - this fixes a bug
213
4. (recommended) Skip a line and add a description of the changes. Include the motivation for making them,
any info about back or forward compatibility, and any rationale of why the change had to be done a certain way.
Example:
We speed up the Remember Me table migration by using a single INSERT INTO ... SELECT query
instead of row-by-row. This migration takes place during the upgrade to 1.9.
Unless your change is trivial/obvious, a description is required.
5. If the commit resolves a GitHub issue, skip a line and add Fixes # followed by the issue number. E.g. Fixes
#1234. You can include multiple issues by separating with commas.
GitHub will auto-close the issue when the commit is merged. If you just want to reference an issue, use Refs
# instead.
When done, your commit message will have the format:
type(component): summary
Optional body
Details about the solution.
Opportunity to call out as breaking change.
Closes/Fixes/Refs #123, #456, #789
We speed up the Remember Me table migration by using a single INSERT INTO ... SELECT query instead of
214
6.3.3 Testing
Elgg has automated tests for both PHP and JavaScript functionality. All new contributions are required to come with
appropriate tests.
PHPUnit Tests
TODO
Jasmine Tests
Test files must be named *Test.js and should go in either js/tests/ or next to their source files in
views/default/**.js. Karma will automatically pick up on new *Test.js files and run those tests.
Test boilerplate
define(function(require) {
var elgg = require('elgg');
describe("This new test", function() {
it("fails automatically", function() {
expect(true).toBe(false);
215
});
});
});
You can also run tests continuously during development so they run on each save:
karma start js/tests/karma.conf.js
If you are copy-pasting code a significant amount of code, consider whether theres an opportunity to reduce duplication by introducing a function, an additional argument, a view, or a new component class.
E.g. If you find views that are identical except for a single value, refactor into a single view that takes an option.
Note: In a bugfix release, some duplication is preferrable to refactoring. Fix bugs in the simplest way possible and
refactor to reduce duplication in the next minor release branch.
Embrace SOLID and GRASP
Use these principles for OO design to solve problems using loosely coupled components, and try to make all components and integration code testable.
Whitespace is free
Dont be afraid to use it to separate blocks of code. Use a single space to separate function params and string concatenation.
216
Variable names
Where possible, have functions/methods return a single type. Use empty values such as array(), , or 0 to indicate no
results.
Be careful where valid return values (like "0") could be interpreted as empty.
Functions not throwing an exception on error should return false upon failure.
Functions returning only boolean should be prefaced with is_ or has_ (eg, elgg_is_logged_in(),
elgg_has_access_to_entity()).
Ternary syntax
Minimize nested blocks and distinct execution paths through code. Use Return Early to reduce nesting levels and
cognitive load when reading code.
Use comments effectively
Good comments describe the why. Good code describes the how. E.g.:
Bad:
// increment $i only when the entity is marked as active.
foreach ($entities as $entity) {
if ($entity->active) {
$i++;
}
}
Good:
217
Always include a comment if its not obvious that something must be done in a certain way. Other developers looking
at the code should be discouraged from refactoring in a way that would break the code.
// Can't use empty()/boolean: "0" is a valid value
if ($str === '') {
register_error(elgg_echo('foo:string_cannot_be_empty'));
forward(REFERER);
}
Commit effectively
Err on the side of atomic commits which are highly focused on changing one aspect of the system.
Avoid mixing in unrelated changes or extensive whitespace changes. Commits with many changes are scary and
make pull requests difficult to review.
Use visual git tools to craft highly precise and readable diffs.
Include tests When at all possible include unit tests for code you add or alter. We use:
PHPUnit for PHP unit tests.
SimpleTest for legacy PHP tests that require use of the database. Our long-term goal is to move all tests to
PHPUnit.
Karma for JavaScript unit tests
Naming tests Break tests up by the behaviors you want to test and use names that describe the behavior. E.g.:
Not so good: One big method testAdd().
Better: Methods testAddingZeroChangesNothing and testAddingNegativeNumberSubtracts
Keep bugfixes simple Avoid the temptation to refactor code for a bugfix release. Doing so tends to introduce
regressions, breaking functionality in what should be a stable release.
PHP guidelines
These are the required coding standards for Elgg core and all bundled plugins. Plugin developers are strongly encouraged to adopt these standards.
Developers should first read the PSR-2 Coding Standard Guide.
Elggs standards extend PSR-2, but differ in the following ways:
Indent using one tab character, not spaces.
Opening braces for classes, methods, and functions must go on the same line.
If a line reaches over 100 characters, consider refactoring (e.g. introduce variables).
218
Include PHPDoc comments on functions and classes (all methods; declared properties when appropriate), including types and descriptions of all parameters.
In lists of @param declarations, the beginnings of variable names and descriptions must line up.
Annotate classes, methods, properties, and functions with @access private unless they are intended for
public use, are already of limited visibility, or are within a class already marked as private.
Use // or /* */ when commenting.
Use only // comments inside function/method bodies.
Naming
Use underscores to separate words in the names of functions, variables, and properties. Method names are
camelCase.
Names of functions for public use must begin with elgg_.
All other function names must begin with _elgg_.
Name globals and constants in ALL_CAPS (ACCESS_FRIENDS, $CONFIG).
Miscellaneous
Good:
echo "Hello, $name!
Remove trailing whitespace at the end of lines. An easy way to do this before you commit is to run php
.scripts/fix_style.php from the installation root.
CSS guidelines
Use shorthand where possible
Bad:
219
background-color: #333333;
background-image: url(...);
background-repeat: repeat-x;
background-position: left 10px;
padding: 2px 9px 2px 9px;
Good:
background: #333 url(...) repeat-x left 10px;
padding: 2px 9px;
Bad:
.example_class {}
Good:
.example-class {}
Bad:
color: white;font-size: smaller;
Good:
color: white;
font-size: smaller;
Property declarations
Good:
color: value;
Vendor prefixes
220
Bad:
-moz-border-radius: 5px;
border: 1px solid #999999;
-webkit-border-radius: 5px;
width: auto;
Good:
border: 1px solid #999999;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
width: auto;
Group subproperties
Bad:
background-color: white;
color: #0054A7;
background-position: 2px -257px;
Good:
background-color: white;
background-position: 2px -257px;
color: #0054A7;
Javascript guidelines
Same formatting standards as PHP apply.
All functions should be in the elgg namespace.
Function expressions should end with a semi-colon.
elgg.ui.toggles = function(event) {
event.preventDefault();
$(target).slideToggle('medium');
};
221
The full list of internal services can be seen in the @property-read declarations at the top of
Elgg\Di\ServiceProvider.
Warning: Avoid performing work in your service constructor, particularly if it requires database queries. Currently PHPUnit tests cannot perform them.
222
8. Add your service key to the array in the $public_services property, e.g. foo => true,
Now your service will be available via property access on the Elgg\Application instance:
// using the public foo service
$three = elgg()->foo->add(1, 2);
Note: For examples, see the config service, including the interface Elgg\Services\Config and the concrete
implementation Elgg\Config.
Its that easy! Grunt will continue running, watching the docs for changes and automatically rebuilding.
223
224
225
cd docs/
make gettext
6.7.1 Benefits
For only $50 per year for individuals or $150 per year for organizations, you can get listed as a supporter on our
supporters page. Elgg supporters are listed there unless they request not to be.
Supporters are able to put this official logo on their site if they wish:
6.7.2 Disclaimer
We operate a no refund policy on supporter subscriptions. If you would like to withdraw your support, go to PayPal
and cancel your subscription. You will not be billed the following year.
Being an Elgg Supporter does not give an individual or organization the right to impersonate, trade as or imply they
are connected to the Elgg project. They can, however, mention that they support the Elgg project.
If you have any questions about this disclaimer, email [email protected].
We reserve the right to remove or refuse a listing without any prior warning at our complete discretion. There is no
refund policy.
If there is no obvious use of Elgg, your site will be linked to with nofollow set.
6.7.3 Sign up
If you would like to become an Elgg supporter:
read the disclaimer above
226
Requirements
1. First new stable minor/major release
2. Prepare and tag the release
3. Update the website
4. Make the announcement
6.8.1 Requirements
SSH access to elgg.org
Commit access to http://github.com/Elgg/Elgg
Admin access to https://community.elgg.org/
Access to Twitter account
Access to G+ page
Node.js and NPM installed
Sphinx installed (easy_install sphinx && easy_install sphinx-intl)
Transifex client installed (easy_install transifex-client)
Transifex account with access to Elgg project
227
MIT:
./build.sh 1.8.5 1.8.5-mit /var/www/www.elgg.org/download/
228
229
230
CHAPTER 7
Appendix
231
Contents
General
Plugin cannot start and has been deactivated or This plugin is invalid
White Page (WSOD)
Page not found
Login token mismatch
Form is missing __token or __ts fields
Maintenance mode
Missing email
Server logs
How does registration work?
User validation
Manually add user
Im making or just installed a new theme, but graphics or other elements arent working
Changing profile fields
Changing registration
How do I change PHP settings using .htaccess?
HTTPS login turned on accidently
Using a test site
500 - Internal Server Error
When I upload a photo or change my profile picture I get a white screen
CSS is missing
Should I edit the database manually?
Internet Explorer (IE) login problem
Emails dont support non-Latin characters
Session length
File is missing an owner
No images
Deprecation warnings
Javascript not working
Security
Is upgrade.php a security concern?
Should I delete install.php?
Filtering
Development
What should I use to edit php code
I dont like the wording of something in Elgg. How do I change it?
How do I find the code that does x?
Debug mode
What events are triggered on every page load?
What variables are reserved by Elgg?
Copy a plugin
7.1.1 General
See also:
Getting Help
232
Chapter 7. Appendix
Plugin cannot start and has been deactivated or This plugin is invalid
This error is usually accompanied by more details explaining why the plugin is invalid. This is usually caused by an
incorrectly installed plugin.
If you are installing a plugin called test, there will be a test directory under mod. In that test directory there needs to
be a start.php file: /mod/test/start.php and a manifest.xml file /mod/test/manifest.xml.
If these files do not exist, it could be caused by:
installing a plugin to the wrong directory
creating a directory under /mod that does not contain a plugin
a bad ftp transfer
unzipping a plugin into an extra directory (myplugin.zip unzips to myplugin/myplugin)
If you are on a Unix-based host and the files exist in the correct directory, check the permissions. Elgg must have read
access to the files and read + execute access on the directories.
White Page (WSOD)
A blank, white page (often called a white screen of death) means there is a PHP syntax error. There are a few possible causes o
corrupted file - try transfering the code again to your server
a call to a php module that was not loaded - this can happen after you install a plugin that requires a specific
module.
bad plugin - not all plugins have been written to the same quality so you should be careful which ones you
install.
To find where the error is occurring, change the .htaccess file to display errors to the browser. Set display_errors to
1 and load the same page again. You should see a PHP error in your browser. Change the setting back once youve
resolved the problem.
Note: If you are using the Developers Tools plugin, go to its settings page and make sure you have Display fatal
PHP errors enabled.
If the white screen is due to a bad plugin, remove the latest plugins that you have installed by deleting their directories
and then reload the page.
Note: You can temporarily disable all plugins by creating an empty file at mod/disabled. You can then disable
the offending module via the administrator tools panel.
If you are getting a WSOD when performing an action, like logging in or posting a blog, but there are no error
messages, its most likely caused by non-printable characters in plugin code. Check the plugin for white spaces/new
lines characters after finishing php tag (?>) and remove them.
Page not found
If you have recently installed your Elgg site, the most likely cause for a page not found error is that mod_rewrite is
not setup correctly on your server. There is information in the Install Troubleshooting page on fixing this. The second
most likely cause is that your site url in your database is incorrect.
233
If youve been running your site for a while and suddenly start getting page not found errors, you need to ask yourself
what has changed. Have you added any plugins? Did you change your server configuration?
To debug a page not found error:
Confirm that the link leading to the missing page is correct. If not, how is that link being generated?
Confirm that the .htaccess rewrite rules are being picked up.
Login token mismatch
If you have to log in twice to your site and the error message after the first attempt says there was a token mismatch
error, the URL in Elggs settings does not match the URL used to access it. The most common cause for this is adding
or removing the www when accessing the site. For example, www.elgg.org vs elgg.org. This causes a problem with
session handling because of the way that web browsers save cookies.
To fix this, you can add rewrite rules. To redirect from www.elgg.org to elgg.org in Apache, the rules might look like:
RewriteCond %{HTTP_HOST} .
RewriteCond %{HTTP_HOST} !^elgg\.org
RewriteRule (.*) http://elgg.org/$1 [R=301,L]
If you dont know how to configure rewrite rules, ask your host for more information.
Form is missing __token or __ts fields
All Elgg actions require a security token, and this error occurs when that token is missing. This is either a problem
with your server configuration or with a 3rd party plugin.
If you experience this on a new installation, make sure that your server is properly configured and your rewrite rules
are correct. If you experience this on an upgrade, make sure you have updated your rewrite rules either in .htaccess
(Apache) or in the server configuration.
If you are experiencing this, disable all 3rd party plugins and try again. Very old plugins for Elgg dont use security
tokens. If the problem goes away when plugins are disabled, its due to a plugin that should be updated by its author.
Maintenance mode
To take your site temporarily offline, go to Administration -> Utilities -> Maintenance Mode. Complete the form and
hit save to disable your site for everyone except admin users.
Missing email
If your users are reporting that validation emails are not showing up, have them check their spam folder. It is possible
that the emails coming from your server are being marked as spam. This depends on many factors such as whether
your hosting provider has a problem with spammers, how your PHP mail configuration is set up, what mail transport
agent your server is using, or your host limiting the number of email that you can send in an hour.
If no one gets email at all, it is quite likely your server is not configured properly for email. Your server needs a
program to send email (called a Mail Transfer Agent - MTA) and PHP must be configured to use the MTA.
To quickly check if PHP and an MTA are correctly configured, create a file on your server with the following content:
234
Chapter 7. Appendix
<?php
$address = "your_email@your_host.com";
$subject = 'Test email.';
$body = 'If you can read this, your email is working.';
echo "Attempting to email $address...<br />";
Be sure to replace your_email@your_host.com with your actual email address. Take care to keep quotes around it!
When you access this page through your web browser, it will attempt to send a test email. This test will let you know
that PHP and your MTA are correctly configured. If it failseither you get an error or you never receive the emailyou
will need to do more investigating and possibly contact your service provider.
Fully configuring an MTA and PHPs email functionality is beyond the scope of this FAQ and you should search the
Internet for more resources on this. Some basic information on php parameters can be found on PHPs site
Server logs
Most likely you are using Apache as your web server. Warnings and errors are written to a log by the web server
and can be useful for debugging problems. You will commonly see two types of log files: access logs and error logs.
Information from PHP and Elgg is written to the server error log.
Linux The error log is probably in /var/log/httpd or /var/log/apache2.
Windows - It is probably inside your Apache directory.
Mac OS - The error log is probably in /var/log/apache2/error_log
If you are using shared hosting without ssh access, your hosting provider may provide a mechanism for obtaining
access to your server logs. You will need to ask them about this.
How does registration work?
With a default setup, this is how registration works:
1. User fills out registration form and submits it
2. User account is created and disabled until validated
3. Email is sent to user with a link to validate the account
4. When a user clicks on the link, the account is validated
5. The user can now log in
Failures during this process include the user entering an incorrect email address, the validation email being marked as
spam, or a user never bothering to validate the account.
235
User validation
By default, all users who self-register must validate their accounts through email. If a user has problems validating an
account, you can validate users manually by going to Administration -> Users -> Unvalidated.
You can remove this requirement by deactivating the User Validation by Email plugin.
Note: Removing validation has some consequences: There is no way to know that a user registered with a working
email address, and it may leave you system open to spammers.
Im making or just installed a new theme, but graphics or other elements arent working
Make sure the theme is at the bottom of the plugin list.
Clear your browser cache and reload the page. To lighten the load on the server, Elgg instructs the browser to rarely
load the CSS file. A new theme will completely change the CSS file and a refresh should cause the browser to request
the CSS file again.
If youre building or modifying a theme, make sure you have disabled the simple and system caches. This can be
done by enabling the Developer Tools plugin, then browsing to Administration -> Develop -> Settings. Once youre
satisfied with the changes, enable the caches or performance will suffer.
Changing profile fields
Within the Administration settings of Elgg is a page for replacing the default profile fields. Elgg by default gives the
administrator two choices:
Use the default profile fields
Replace the default with a set of custom profile fields
You cannot add new profile fields to the default ones. Adding a new profile field through the replace profile fields
option clears the default ones. Before letting in users, it is best to determine what profile fields you want, what field
types they should be, and the order they should appear. You cannot change the field type or order or delete fields after
they have been created without wiping the entire profile blank.
More flexibility can be gained through plugins. There is at least two plugins on the community site that enable you to
have more control over profile fields. The Profile Manager plugin has become quite popular in the Elgg community. It
lets you add new profile fields whenever you want, change the order, group profile fields, and add them to registration.
Changing registration
The registration process can be changed through a plugin. Everything about registration can be changed: the look and
feel, different registration fields, additional validation of the fields, additional steps and so on. These types of changes
require some basic knowledge of HTML, CSS, PHP.
236
Chapter 7. Appendix
Another option is to use the Profile Manager plugin that lets you add fields to both user profiles and the registration
form.
Create the plugin skeleton Plugin skeleton
Changing registration display Override the account/forms/register view
Changing the registration action handler You can write your own action to create the users account
How do I change PHP settings using .htaccess?
You may want to change php settings in your .htaccess file. This is especially true if your hosting provider does
not give you access to the servers php.ini file. The variables could be related to file upload size limits, security,
session length, or any number of other php attributes. For examples of how to do this, see the PHP documentation on
this.
HTTPS login turned on accidently
If you have turned on HTTPS login but do not have SSL configured, you are now locked out of your Elgg install. To
turn off this configuration parameter, you will need to edit your database. Use a tool like phpMyAdmin to view your
database. Select the config table and delete the row that has the name https_login.
Using a test site
It is recommended to always try out new releases or new plugins on a test site before running them on a production
site (a site with actual users). The easiest way to do this is to maintain a separate install of Elgg with dummy accounts.
When testing changes it is important to use dummy accounts that are not admins to test what your users will see.
A more realistic test is to mirror the content from your production site to your test site. Following the instructions for
duplicating a site. Then make sure you prevent emails from being sent to your users. You could write a small plugin
that redirects all email to your own account (be aware of plugins that include their own custom email sending code so
youll have to modify those plugins). After this is done you can view all of the content to make sure the upgrade or
new plugin is functioning as desired and is not breaking anything. If this process sounds overwhelming, please stick
with running a simple test site.
500 - Internal Server Error
What is it?
A 500 - Internal Server Error means the web server experienced a problem serving a request.
See also:
The Wikipedia page on HTTP status codes
Possible causes
Web server configuration The most common cause for this is an incorrectly configured server. If you edited the
.htaccess file and added something incorrect, Apache will send a 500 error.
Permissions on files It could also be a permissions problem on a file. Apache needs to be able to read Elggs files.
Using permissions 755 on directories and 644 on files will allow Apache to read the files.
237
Sometimes people install Elgg so that the base URL is localhost and then try to view the site using a hostname. In
this case, the browser wont be able to load the CSS file. Try viewing the source of the web page and copying the link
for the CSS file. Paste that into your browser. If you get a 404 error, it is likely this is your problem. You will need to
change the base URL of your site.
Syntax error
Elgg stores its CSS as PHP code to provide flexibility and power. If there is a syntax error, the CSS file served to the
browser may be blank. Disabling non-bundled plugins is the recommended first step.
Rewrite rules errors
A bad .htaccess file could also result in a 404 error when requesting the CSS file. This could happen when doing
an upgrade and forgetting to also upgrade .htaccess.
Should I edit the database manually?
Warning: No, you should never manually edit the database!
Yes.
Can I add extra fields to tables in the database?
(AKA: I dont understand the Elgg data model so Im going to add columns. Will you help?)
No, this is a bad idea. Learn the data model and you will see that unless its a very specific and highly customized
installation, you can do everything you need within Elggs current data model.
I want to remove users. Cant I just delete them from the elgg_users_entity table?
No, it will corrupt your database. Delete them through the site.
I want to remove spam. Cant I just search and delete it from the elgg_objects_entity table?
Chapter 7. Appendix
Someone on the community site told me to edit the database manually. Should I?
Who was it? Is it someone experienced with Elgg, like one of the core developers or a well-known plugin author?
Did he or she give you clear and specific instructions on what to edit? If you dont know who it is, or if you cant
understand or arent comfortable following the instructions, do not edit the database manually.
I know PHP and MySQL and have a legitimate reason to edit the database. Is it okay to manually edit the
database?
Make sure you understand Elggs data model and schema first. Make a backup, edit carefully, then test copiously.
Internet Explorer (IE) login problem
Canonical URL
IE does not like working with sites that use both http://example.org and http://www.example.org. It stores multiple
cookies and this causes problems. Best to only use one base URL. For details on how to do this see Login token
mismatch error.
Chrome Frame
Using the chrome frame within IE can break the login process.
Emails dont support non-Latin characters
In order to support non-Latin characters, (such as Cyrillic or Chinese) Elgg requires multibyte string support to be
compiled into PHP.
On many installs (e.g. Debian & Ubuntu) this is turned on by default. If it is not, you need to turn it on (or recompile
PHP to include it). To check whether your server supports multibyte strings, check phpinfo.
Session length
Session length is controlled by your php configuration. You will first need to locate your php.ini file. In that file
will be several session variables. A complete list and what they do can be found in the php manual.
File is missing an owner
There are three causes for this error. You could have an entity in your database that has an owner_guid of 0. This
should be extremely rare and may only occur if your database/server crashes during a write operation.
The second cause would be an entity where the owner no longer exists. This could occur if a plugin is turned off
that was involved in the creation of the entity and then the owner is deleted but the delete operation failed (because
the plugin is turned off). If you can figure out entity is causing this, look in your entities table and change the
owner_guid to your own and then you can delete the entity through Elgg.
Warning: Reed the section Should I edit the database manually?. Be very carefull when editing the database
directly. It can break your site. Always make a backup before doing this.
239
The third cause is a user not having a username. This also indicates a database problem as this should not be possible.
If it does occur, you could see this error when viewing a list of users (such as with the Members plugin). To fix, check
your users_entity table for users without a username and if so, create a fake a username for that person. You
should probably then delete the user through Elgg.
Fixes
Database Validator plugin will check your database for these causes and provide an option to fix them. Be sure to
backup the database before you try the fix option.
No images
If profile images, group images, or other files have stopped working on your site it is likely due to a misconfiguration,
especially if you have migrated to a new server.
These are the most common misconfigurations that cause images and other files to stop working.
Wrong path for data directory
Make sure the data directorys path is correct in the Site Administration admin area. It should have a trailing slash.
Wrong permissions on the data directory
Check the permissions for the data directory. The data directory should be readable and writeable by the web server
user.
Different timezone
If you migrated an installation and need to change your data directory path, be sure to update the SQL for the filestore
location as documented in the Duplicate Installation instructions.
Deprecation warnings
If you are seeing many deprecation warnings that say things like
Deprecated in 1.7: extend_view() was deprecated by elgg_extend_view()!
240
Chapter 7. Appendix
then you are using a plugin that was written for an older version of Elgg. This means the plugin is using functions
that are scheduled to be removed in a future version of Elgg. You can ask the plugin developer if the plugin will be
updated or you can update the plugin yourself. If neither of those are likely to happen, you should not use that plugin.
Javascript not working
If the user hover menu stops working or you cannot dismiss system messages, that means JavaScript is broken on
your site. This usually due to a plugin having bad JavaScript code. You should find the plugin causing the problem
and disable it. You can do this be disabling non-bundled plugins one at a time until the problem goes away. Another
approach is disabling all non-bundled plugins and then enabling them one by one until the problem occurs again.
Most web browsers will give you a hint as to what is breaking the JavaScript code. They often have a console for
JavaScript errors or an advanced mode for displaying errors. Once you see the error message, you may have an easier
time locating the problem.
7.1.2 Security
Is upgrade.php a security concern?
Upgrade.php is a file used to run code and database upgrades. It is in the root of the directory and doesnt require a
logged in account to access. On a fully upgraded site, running the file will only reset the caches and exit, so this is not
a security concern.
If you are still concerned, you can either delete, move, or change permissions on the file until you need to upgrade.
Should I delete install.php?
This file is used to install Elgg and doesnt need to be deleted. The file checks if Elgg is already installed and forwards
the user to the front page if it is.
Filtering
Filtering is used in Elgg to make XSS attacks more difficult. The purpose of the filtering is to remove Javascript and
other dangerous input from users.
Filtering is performed through the function filter_tags(). This function takes in a string and returns a filtered
string. It triggers a validate, input plugin hook. By default Elgg comes with the htmLawed filtering code as a plugin.
Developers can drop in any additional or replacement filtering code as a plugin.
The filter_tags() function is called on any user input as long as the input is obtained through a call to
get_input(). If for some reason a developer did not want to perform the default filtering on some user input,
the get_input() function has a parameter for turning off filtering.
7.1.3 Development
What should I use to edit php code
There are two main options: text editor or integrated development environment (IDE).
241
Text Editor
If you are new to software development or do not have much experience with IDEs, using a text editor will get you up
and running the quickest. At a minimum, you will want one that does syntax highlighting to make the code easier to
read. If you think you might submit patches to the bug tracker, you will want to make sure that your text editor does
not change line endings. If you are using Windows, Notepad++ is a good choice. If you are on a Mac, TextWrangler
is a popular choice. You could also give TextMate a try.
Integrated Development Environment
An IDE does just what its name implies: it includes a set of tools that you would normally use separately. Most
IDEs will include source code control which will allow you to directly commit and update your code from your cvs
repository. It may have an FTP client built into it to make the transfer of files to a remote server easier. It will have
syntax checking to catch errors before you try to execute the code on a server.
The two most popular free IDEs for PHP developers are Eclipse and NetBeans. Eclipse has two different plugins for
working with PHP code: PDT and PHPEclipse.
I dont like the wording of something in Elgg. How do I change it?
The best way to do this is with a plugin.
Create the plugin skeleton
Plugin skeleton
Locate the string that you want to change
All the strings that a user sees should be in the /languages directory or in a plugins languages directory
(/mod/<plugin name>/languages). This is done so that it is easy to change what language Elgg uses. For
more information on this see the developer documentation on Internationalization .
To find the string use grep or a text editor that provides searching through files to locate the string. (A good text editor
for Windows is Notepad++ ) Lets say we want to change the string Add friend to Make a new friend. The grep
command to find this string would be grep -r "Add friend" *. Using Notepad++ , you would use the Find
in files command. You would search for the string, set the filter to *.php, set the directory to the base directory of
Elgg, and make sure it searches all subdirectories. You might want to set it to be case sensitive also.
You should locate the string Add friend in /languages/en.php. You should see something like this in the file:
'friend:add' => "Add friend",
This means every time Elgg sees friend:add it replaces it with Add friend. We want to change the definition of
friend:add.
Override the string
To override this definition, we will add a languages file to the plugin that we built in the first step.
1. Create a new directory: /mod/<your plugin name>/languages
2. Create a file in that directory called en.php
242
Chapter 7. Appendix
Make sure that you do not have any spaces or newlines before the <?php.
Youre done now and should be able to enable the plugin and see the change. If you are override the language of a
plugin, make sure your plugin is loaded after the one you are trying to modify. The loading order is determined in
the Tools Administration page of the admin section. As you find more things that youd like to change, you can keep
adding them to this plugin.
How do I find the code that does x?
The best way to find the code that does something that you would like to change is to use grep or a similar search tool.
If you do not have grep as a part of your operating system, you will want to install a grep tool or use a text-editor/IDE
that has good searching in files. Notepad++ is a good choice for Windows users. Eclipse with PHP and NetBeans are
good choices for any platform.
String Example
Lets say that you want to find where the Log In box code is located. A string from the Log In box that
should be fairly unique is Remember me. Grep for that string. You will find that it is only used in
the en.php file in the /languages directory. There it is used to define the Internationalization string
user:persistent. Grep for that string now. You will find it in two places: the same en.php language file
and in /views/default/forms/login.php. The latter defines the html code that makes up the Log In box.
Action Example
Lets say that you want to find the code that is run when a user clicks on the Save button when arranging widgets on
a profile page. View the Profile page for a test user. Use Firebug to drill down through the html of the page until you
come to the action of the edit widgets form. Youll see the url from the base is action/widgets/move.
Grep on widgets/move and two files are returned.
One is the JavaScript code for the widgets :
/js/lib/ui.widgets.js. The other one, /engine/lib/widgets.php, is where the action is registered
using elgg_register_action(widgets/reorder). You may not be familiar with that function in which
case, you should look it up at the API reference. Do a search on the function and it returns the documentation on the
function. This tells you that the action is in the default location since a file location was not specified. The default
location for actions is /actions so you will find the file at /actions/widgets/move.php.
Debug mode
During the installation process you might have noticed a checkbox that controlled whether debug mode was turned
on or off. This setting can also be changed on the Site Administration page. Debug mode writes a lot of extra data to
your php log. For example, when running in this mode every query to the database is written to your logs. It may be
useful for debugging a problem though it can produce an overwhelming amount of data that may not be related to the
problem at all. You may want to experiment with this mode to understand what it does, but make sure you run Elgg in
normal mode on a production server.
243
Warning: Because of the amount of data being logged, dont enable this on a production server as it can fill up
the log files really quick.
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
Chapter 7. Appendix
4. pagesetup, system
5. shutdown, system
The boot, system event is triggered before the plugins get loaded. There does not appear to be any difference between
the timing of the next two events: plugins_boot, system and init, system so plugins tend to use init, system. This
event is triggered in Elgg\Application::bootCore. The pagesetup, system event is thrown the first time
elgg_view() is called. Some pages like the default index.php do not call elgg_view() so it is not triggered
for them. The shutdown, system event is triggered after the page has been sent to the requester and is handled through
the PHP function register_shutdown_function().
There are other events that are triggered by the Elgg core but they happen occasionally (such as when a user logs in).
What variables are reserved by Elgg?
$CONFIG
$vars
$autofeed
$_GET[action] / $_POST[action]
$viewtype
Copy a plugin
There are many questions asked about how to copy a plugin. Lets say you want to copy the blog plugin in order to
run one plugin called blog and another called poetry. This is not difficult but it does require a lot of work. You
would need to
change the directory name
change the names of every function (having two functions causes PHP to crash)
change the name of every view (so as not to override the views on the original plugin)
change any data model subtypes
change the language file
change anything else that was specific to the original plugin
Note: If you are trying to clone the groups plugin, you will have the additional difficulty that the group plugin does
not set a subtype.
7.1.4 General
See also:
Getting Help
Plugin cannot start and has been deactivated or This plugin is invalid
This error is usually accompanied by more details explaining why the plugin is invalid. This is usually caused by an
incorrectly installed plugin.
245
If you are installing a plugin called test, there will be a test directory under mod. In that test directory there needs to
be a start.php file: /mod/test/start.php and a manifest.xml file /mod/test/manifest.xml.
If these files do not exist, it could be caused by:
installing a plugin to the wrong directory
creating a directory under /mod that does not contain a plugin
a bad ftp transfer
unzipping a plugin into an extra directory (myplugin.zip unzips to myplugin/myplugin)
If you are on a Unix-based host and the files exist in the correct directory, check the permissions. Elgg must have read
access to the files and read + execute access on the directories.
White Page (WSOD)
A blank, white page (often called a white screen of death) means there is a PHP syntax error. There are a few possible causes o
corrupted file - try transfering the code again to your server
a call to a php module that was not loaded - this can happen after you install a plugin that requires a specific
module.
bad plugin - not all plugins have been written to the same quality so you should be careful which ones you
install.
To find where the error is occurring, change the .htaccess file to display errors to the browser. Set display_errors to
1 and load the same page again. You should see a PHP error in your browser. Change the setting back once youve
resolved the problem.
Note: If you are using the Developers Tools plugin, go to its settings page and make sure you have Display fatal
PHP errors enabled.
If the white screen is due to a bad plugin, remove the latest plugins that you have installed by deleting their directories
and then reload the page.
Note: You can temporarily disable all plugins by creating an empty file at mod/disabled. You can then disable
the offending module via the administrator tools panel.
If you are getting a WSOD when performing an action, like logging in or posting a blog, but there are no error
messages, its most likely caused by non-printable characters in plugin code. Check the plugin for white spaces/new
lines characters after finishing php tag (?>) and remove them.
Page not found
If you have recently installed your Elgg site, the most likely cause for a page not found error is that mod_rewrite is
not setup correctly on your server. There is information in the Install Troubleshooting page on fixing this. The second
most likely cause is that your site url in your database is incorrect.
If youve been running your site for a while and suddenly start getting page not found errors, you need to ask yourself
what has changed. Have you added any plugins? Did you change your server configuration?
To debug a page not found error:
Confirm that the link leading to the missing page is correct. If not, how is that link being generated?
246
Chapter 7. Appendix
Confirm that the .htaccess rewrite rules are being picked up.
Login token mismatch
If you have to log in twice to your site and the error message after the first attempt says there was a token mismatch
error, the URL in Elggs settings does not match the URL used to access it. The most common cause for this is adding
or removing the www when accessing the site. For example, www.elgg.org vs elgg.org. This causes a problem with
session handling because of the way that web browsers save cookies.
To fix this, you can add rewrite rules. To redirect from www.elgg.org to elgg.org in Apache, the rules might look like:
RewriteCond %{HTTP_HOST} .
RewriteCond %{HTTP_HOST} !^elgg\.org
RewriteRule (.*) http://elgg.org/$1 [R=301,L]
If you dont know how to configure rewrite rules, ask your host for more information.
Form is missing __token or __ts fields
All Elgg actions require a security token, and this error occurs when that token is missing. This is either a problem
with your server configuration or with a 3rd party plugin.
If you experience this on a new installation, make sure that your server is properly configured and your rewrite rules
are correct. If you experience this on an upgrade, make sure you have updated your rewrite rules either in .htaccess
(Apache) or in the server configuration.
If you are experiencing this, disable all 3rd party plugins and try again. Very old plugins for Elgg dont use security
tokens. If the problem goes away when plugins are disabled, its due to a plugin that should be updated by its author.
Maintenance mode
To take your site temporarily offline, go to Administration -> Utilities -> Maintenance Mode. Complete the form and
hit save to disable your site for everyone except admin users.
Missing email
If your users are reporting that validation emails are not showing up, have them check their spam folder. It is possible
that the emails coming from your server are being marked as spam. This depends on many factors such as whether
your hosting provider has a problem with spammers, how your PHP mail configuration is set up, what mail transport
agent your server is using, or your host limiting the number of email that you can send in an hour.
If no one gets email at all, it is quite likely your server is not configured properly for email. Your server needs a
program to send email (called a Mail Transfer Agent - MTA) and PHP must be configured to use the MTA.
To quickly check if PHP and an MTA are correctly configured, create a file on your server with the following content:
<?php
$address = "your_email@your_host.com";
$subject = 'Test email.';
247
Be sure to replace your_email@your_host.com with your actual email address. Take care to keep quotes around it!
When you access this page through your web browser, it will attempt to send a test email. This test will let you know
that PHP and your MTA are correctly configured. If it failseither you get an error or you never receive the emailyou
will need to do more investigating and possibly contact your service provider.
Fully configuring an MTA and PHPs email functionality is beyond the scope of this FAQ and you should search the
Internet for more resources on this. Some basic information on php parameters can be found on PHPs site
Server logs
Most likely you are using Apache as your web server. Warnings and errors are written to a log by the web server
and can be useful for debugging problems. You will commonly see two types of log files: access logs and error logs.
Information from PHP and Elgg is written to the server error log.
Linux The error log is probably in /var/log/httpd or /var/log/apache2.
Windows - It is probably inside your Apache directory.
Mac OS - The error log is probably in /var/log/apache2/error_log
If you are using shared hosting without ssh access, your hosting provider may provide a mechanism for obtaining
access to your server logs. You will need to ask them about this.
How does registration work?
With a default setup, this is how registration works:
1. User fills out registration form and submits it
2. User account is created and disabled until validated
3. Email is sent to user with a link to validate the account
4. When a user clicks on the link, the account is validated
5. The user can now log in
Failures during this process include the user entering an incorrect email address, the validation email being marked as
spam, or a user never bothering to validate the account.
User validation
By default, all users who self-register must validate their accounts through email. If a user has problems validating an
account, you can validate users manually by going to Administration -> Users -> Unvalidated.
You can remove this requirement by deactivating the User Validation by Email plugin.
Note: Removing validation has some consequences: There is no way to know that a user registered with a working
email address, and it may leave you system open to spammers.
248
Chapter 7. Appendix
Im making or just installed a new theme, but graphics or other elements arent working
Make sure the theme is at the bottom of the plugin list.
Clear your browser cache and reload the page. To lighten the load on the server, Elgg instructs the browser to rarely
load the CSS file. A new theme will completely change the CSS file and a refresh should cause the browser to request
the CSS file again.
If youre building or modifying a theme, make sure you have disabled the simple and system caches. This can be
done by enabling the Developer Tools plugin, then browsing to Administration -> Develop -> Settings. Once youre
satisfied with the changes, enable the caches or performance will suffer.
Changing profile fields
Within the Administration settings of Elgg is a page for replacing the default profile fields. Elgg by default gives the
administrator two choices:
Use the default profile fields
Replace the default with a set of custom profile fields
You cannot add new profile fields to the default ones. Adding a new profile field through the replace profile fields
option clears the default ones. Before letting in users, it is best to determine what profile fields you want, what field
types they should be, and the order they should appear. You cannot change the field type or order or delete fields after
they have been created without wiping the entire profile blank.
More flexibility can be gained through plugins. There is at least two plugins on the community site that enable you to
have more control over profile fields. The Profile Manager plugin has become quite popular in the Elgg community. It
lets you add new profile fields whenever you want, change the order, group profile fields, and add them to registration.
Changing registration
The registration process can be changed through a plugin. Everything about registration can be changed: the look and
feel, different registration fields, additional validation of the fields, additional steps and so on. These types of changes
require some basic knowledge of HTML, CSS, PHP.
Another option is to use the Profile Manager plugin that lets you add fields to both user profiles and the registration
form.
Create the plugin skeleton Plugin skeleton
Changing registration display Override the account/forms/register view
Changing the registration action handler You can write your own action to create the users account
249
A 500 - Internal Server Error means the web server experienced a problem serving a request.
See also:
The Wikipedia page on HTTP status codes
Possible causes
Web server configuration The most common cause for this is an incorrectly configured server. If you edited the
.htaccess file and added something incorrect, Apache will send a 500 error.
Permissions on files It could also be a permissions problem on a file. Apache needs to be able to read Elggs files.
Using permissions 755 on directories and 644 on files will allow Apache to read the files.
When I upload a photo or change my profile picture I get a white screen
Most likely you dont have the PHP GD library installed or configured properly. You may need assistance from the
administrator of your server.
250
Chapter 7. Appendix
CSS is missing
Wrong URL
Sometimes people install Elgg so that the base URL is localhost and then try to view the site using a hostname. In
this case, the browser wont be able to load the CSS file. Try viewing the source of the web page and copying the link
for the CSS file. Paste that into your browser. If you get a 404 error, it is likely this is your problem. You will need to
change the base URL of your site.
Syntax error
Elgg stores its CSS as PHP code to provide flexibility and power. If there is a syntax error, the CSS file served to the
browser may be blank. Disabling non-bundled plugins is the recommended first step.
Rewrite rules errors
A bad .htaccess file could also result in a 404 error when requesting the CSS file. This could happen when doing
an upgrade and forgetting to also upgrade .htaccess.
Should I edit the database manually?
Warning: No, you should never manually edit the database!
Yes.
Can I add extra fields to tables in the database?
(AKA: I dont understand the Elgg data model so Im going to add columns. Will you help?)
No, this is a bad idea. Learn the data model and you will see that unless its a very specific and highly customized
installation, you can do everything you need within Elggs current data model.
I want to remove users. Cant I just delete them from the elgg_users_entity table?
No, it will corrupt your database. Delete them through the site.
I want to remove spam. Cant I just search and delete it from the elgg_objects_entity table?
251
Someone on the community site told me to edit the database manually. Should I?
Who was it? Is it someone experienced with Elgg, like one of the core developers or a well-known plugin author?
Did he or she give you clear and specific instructions on what to edit? If you dont know who it is, or if you cant
understand or arent comfortable following the instructions, do not edit the database manually.
I know PHP and MySQL and have a legitimate reason to edit the database. Is it okay to manually edit the
database?
Make sure you understand Elggs data model and schema first. Make a backup, edit carefully, then test copiously.
Internet Explorer (IE) login problem
Canonical URL
IE does not like working with sites that use both http://example.org and http://www.example.org. It stores multiple
cookies and this causes problems. Best to only use one base URL. For details on how to do this see Login token
mismatch error.
Chrome Frame
Using the chrome frame within IE can break the login process.
Emails dont support non-Latin characters
In order to support non-Latin characters, (such as Cyrillic or Chinese) Elgg requires multibyte string support to be
compiled into PHP.
On many installs (e.g. Debian & Ubuntu) this is turned on by default. If it is not, you need to turn it on (or recompile
PHP to include it). To check whether your server supports multibyte strings, check phpinfo.
Session length
Session length is controlled by your php configuration. You will first need to locate your php.ini file. In that file
will be several session variables. A complete list and what they do can be found in the php manual.
File is missing an owner
There are three causes for this error. You could have an entity in your database that has an owner_guid of 0. This
should be extremely rare and may only occur if your database/server crashes during a write operation.
The second cause would be an entity where the owner no longer exists. This could occur if a plugin is turned off
that was involved in the creation of the entity and then the owner is deleted but the delete operation failed (because
the plugin is turned off). If you can figure out entity is causing this, look in your entities table and change the
owner_guid to your own and then you can delete the entity through Elgg.
Warning: Reed the section Should I edit the database manually?. Be very carefull when editing the database
directly. It can break your site. Always make a backup before doing this.
252
Chapter 7. Appendix
The third cause is a user not having a username. This also indicates a database problem as this should not be possible.
If it does occur, you could see this error when viewing a list of users (such as with the Members plugin). To fix, check
your users_entity table for users without a username and if so, create a fake a username for that person. You
should probably then delete the user through Elgg.
Fixes
Database Validator plugin will check your database for these causes and provide an option to fix them. Be sure to
backup the database before you try the fix option.
No images
If profile images, group images, or other files have stopped working on your site it is likely due to a misconfiguration,
especially if you have migrated to a new server.
These are the most common misconfigurations that cause images and other files to stop working.
Wrong path for data directory
Make sure the data directorys path is correct in the Site Administration admin area. It should have a trailing slash.
Wrong permissions on the data directory
Check the permissions for the data directory. The data directory should be readable and writeable by the web server
user.
Different timezone
If you migrated an installation and need to change your data directory path, be sure to update the SQL for the filestore
location as documented in the Duplicate Installation instructions.
Deprecation warnings
If you are seeing many deprecation warnings that say things like
Deprecated in 1.7: extend_view() was deprecated by elgg_extend_view()!
253
then you are using a plugin that was written for an older version of Elgg. This means the plugin is using functions
that are scheduled to be removed in a future version of Elgg. You can ask the plugin developer if the plugin will be
updated or you can update the plugin yourself. If neither of those are likely to happen, you should not use that plugin.
Javascript not working
If the user hover menu stops working or you cannot dismiss system messages, that means JavaScript is broken on
your site. This usually due to a plugin having bad JavaScript code. You should find the plugin causing the problem
and disable it. You can do this be disabling non-bundled plugins one at a time until the problem goes away. Another
approach is disabling all non-bundled plugins and then enabling them one by one until the problem occurs again.
Most web browsers will give you a hint as to what is breaking the JavaScript code. They often have a console for
JavaScript errors or an advanced mode for displaying errors. Once you see the error message, you may have an easier
time locating the problem.
7.1.5 Security
Is upgrade.php a security concern?
Upgrade.php is a file used to run code and database upgrades. It is in the root of the directory and doesnt require a
logged in account to access. On a fully upgraded site, running the file will only reset the caches and exit, so this is not
a security concern.
If you are still concerned, you can either delete, move, or change permissions on the file until you need to upgrade.
Should I delete install.php?
This file is used to install Elgg and doesnt need to be deleted. The file checks if Elgg is already installed and forwards
the user to the front page if it is.
Filtering
Filtering is used in Elgg to make XSS attacks more difficult. The purpose of the filtering is to remove Javascript and
other dangerous input from users.
Filtering is performed through the function filter_tags(). This function takes in a string and returns a filtered
string. It triggers a validate, input plugin hook. By default Elgg comes with the htmLawed filtering code as a plugin.
Developers can drop in any additional or replacement filtering code as a plugin.
The filter_tags() function is called on any user input as long as the input is obtained through a call to
get_input(). If for some reason a developer did not want to perform the default filtering on some user input,
the get_input() function has a parameter for turning off filtering.
7.1.6 Development
What should I use to edit php code
There are two main options: text editor or integrated development environment (IDE).
254
Chapter 7. Appendix
Text Editor
If you are new to software development or do not have much experience with IDEs, using a text editor will get you up
and running the quickest. At a minimum, you will want one that does syntax highlighting to make the code easier to
read. If you think you might submit patches to the bug tracker, you will want to make sure that your text editor does
not change line endings. If you are using Windows, Notepad++ is a good choice. If you are on a Mac, TextWrangler
is a popular choice. You could also give TextMate a try.
Integrated Development Environment
An IDE does just what its name implies: it includes a set of tools that you would normally use separately. Most
IDEs will include source code control which will allow you to directly commit and update your code from your cvs
repository. It may have an FTP client built into it to make the transfer of files to a remote server easier. It will have
syntax checking to catch errors before you try to execute the code on a server.
The two most popular free IDEs for PHP developers are Eclipse and NetBeans. Eclipse has two different plugins for
working with PHP code: PDT and PHPEclipse.
I dont like the wording of something in Elgg. How do I change it?
The best way to do this is with a plugin.
Create the plugin skeleton
Plugin skeleton
Locate the string that you want to change
All the strings that a user sees should be in the /languages directory or in a plugins languages directory
(/mod/<plugin name>/languages). This is done so that it is easy to change what language Elgg uses. For
more information on this see the developer documentation on Internationalization .
To find the string use grep or a text editor that provides searching through files to locate the string. (A good text editor
for Windows is Notepad++ ) Lets say we want to change the string Add friend to Make a new friend. The grep
command to find this string would be grep -r "Add friend" *. Using Notepad++ , you would use the Find
in files command. You would search for the string, set the filter to *.php, set the directory to the base directory of
Elgg, and make sure it searches all subdirectories. You might want to set it to be case sensitive also.
You should locate the string Add friend in /languages/en.php. You should see something like this in the file:
'friend:add' => "Add friend",
This means every time Elgg sees friend:add it replaces it with Add friend. We want to change the definition of
friend:add.
Override the string
To override this definition, we will add a languages file to the plugin that we built in the first step.
1. Create a new directory: /mod/<your plugin name>/languages
2. Create a file in that directory called en.php
255
Make sure that you do not have any spaces or newlines before the <?php.
Youre done now and should be able to enable the plugin and see the change. If you are override the language of a
plugin, make sure your plugin is loaded after the one you are trying to modify. The loading order is determined in
the Tools Administration page of the admin section. As you find more things that youd like to change, you can keep
adding them to this plugin.
How do I find the code that does x?
The best way to find the code that does something that you would like to change is to use grep or a similar search tool.
If you do not have grep as a part of your operating system, you will want to install a grep tool or use a text-editor/IDE
that has good searching in files. Notepad++ is a good choice for Windows users. Eclipse with PHP and NetBeans are
good choices for any platform.
String Example
Lets say that you want to find where the Log In box code is located. A string from the Log In box that
should be fairly unique is Remember me. Grep for that string. You will find that it is only used in
the en.php file in the /languages directory. There it is used to define the Internationalization string
user:persistent. Grep for that string now. You will find it in two places: the same en.php language file
and in /views/default/forms/login.php. The latter defines the html code that makes up the Log In box.
Action Example
Lets say that you want to find the code that is run when a user clicks on the Save button when arranging widgets on
a profile page. View the Profile page for a test user. Use Firebug to drill down through the html of the page until you
come to the action of the edit widgets form. Youll see the url from the base is action/widgets/move.
Grep on widgets/move and two files are returned.
One is the JavaScript code for the widgets :
/js/lib/ui.widgets.js. The other one, /engine/lib/widgets.php, is where the action is registered
using elgg_register_action(widgets/reorder). You may not be familiar with that function in which
case, you should look it up at the API reference. Do a search on the function and it returns the documentation on the
function. This tells you that the action is in the default location since a file location was not specified. The default
location for actions is /actions so you will find the file at /actions/widgets/move.php.
Debug mode
During the installation process you might have noticed a checkbox that controlled whether debug mode was turned
on or off. This setting can also be changed on the Site Administration page. Debug mode writes a lot of extra data to
your php log. For example, when running in this mode every query to the database is written to your logs. It may be
useful for debugging a problem though it can produce an overwhelming amount of data that may not be related to the
problem at all. You may want to experiment with this mode to understand what it does, but make sure you run Elgg in
normal mode on a production server.
256
Chapter 7. Appendix
Warning: Because of the amount of data being logged, dont enable this on a production server as it can fill up
the log files really quick.
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
14:27:20]
257
4. pagesetup, system
5. shutdown, system
The boot, system event is triggered before the plugins get loaded. There does not appear to be any difference between
the timing of the next two events: plugins_boot, system and init, system so plugins tend to use init, system. This
event is triggered in Elgg\Application::bootCore. The pagesetup, system event is thrown the first time
elgg_view() is called. Some pages like the default index.php do not call elgg_view() so it is not triggered
for them. The shutdown, system event is triggered after the page has been sent to the requester and is handled through
the PHP function register_shutdown_function().
There are other events that are triggered by the Elgg core but they happen occasionally (such as when a user logs in).
What variables are reserved by Elgg?
$CONFIG
$vars
$autofeed
$_GET[action] / $_POST[action]
$viewtype
Copy a plugin
There are many questions asked about how to copy a plugin. Lets say you want to copy the blog plugin in order to
run one plugin called blog and another called poetry. This is not difficult but it does require a lot of work. You
would need to
change the directory name
change the names of every function (having two functions causes PHP to crash)
change the name of every view (so as not to override the views on the original plugin)
change any data model subtypes
change the language file
change anything else that was specific to the original plugin
Note: If you are trying to clone the groups plugin, you will have the additional difficulty that the group plugin does
not set a subtype.
7.2 Roadmap
What direction is the project going? What exciting new features are coming soon?
We do not publish detailed roadmaps, but its possible to get a sense for our general direction by utilizing the following
resources:
Our feedback and planning group is used to host early discussion about what will be worked on next.
Our Github milestones represent a general direction for the future releases of Elgg. This is the closest thing to a
traditional roadmap that we have.
258
Chapter 7. Appendix
Github pull requests will give you a good idea of whats currently being developed, but nothing is sure until the
PR is actually checked in.
We use the developer blog to post announcements of features that have recently been checked in to our development branch, which gives the surest indication of what features will be available in the next release.
7.2.1 Values
We have several overarching goals/values that affect the direction of Elgg. Enhancements generally must promote
these values in order to be accepted.
Accessibility
Elgg-based sites should be usable by anyone anywhere. That means well always strive to make Elgg:
Device-agnostic mobile, tablet, desktop, etc. friendly
Language-agnostic i18n, RTL, etc.
Capability-agnostic touch, keyboard, screen-reader friendly
Testability
We want to make manual testing unnecessary for core developers, plugin authors, and site administrators by promoting and enabling fast, automated testing at every level of the Elgg stack.
We think APIs are broken if they require plugin authors to write untestable code. We know there are a lot of violations
of this principle in core currently and are working to fix it.
We look forward to a world where the core developers do not need to do any manual testing to verify the correctness
of code contributed to Elgg. Similarly, we envision a world where site administrators can upgrade and install new
plugins with confidence that everything works well together.
TODO: other goals/values?
7.2.2 FAQ
When will feature X be implemented?
We cannot promise when features will get implemented because new features are checked into Elgg only when someone is motivated enough to implement the feature and submit a pull request. The best we can do is tell you to look out
for what features existing developers have expressed interest in working on.
The best way to ensure a feature gets implemented is to discuss it with the core team and implement it yourself. See
our Contributor Guides guide if youre interested. We love new contributors!
Do not rely on future enhancements if youre on the fence as to whether to use Elgg. Evaluate it given the current
feature set. Upcoming features will almost certainly not materialize within your timeline.
When is version X.Y.Z going to be released?
The next version will be released when the core team feels its ready and has time to cut the release.
http://github.com/Elgg/Elgg/issues/milestones will give you some rough ideas of timeline.
7.2. Roadmap
259
260
Chapter 7. Appendix
See also:
Release Policy
Below is a table outlining the specifics for each release:
Version
1.8
1.9
1.10
1.11
We dont have a support policy for major releases (x.0.0) yet because weve never done one.
7.5 History
The name comes from a town in Switzerland. It also means elk or moose in Danish.
Elggs initial funding was by a company called Curverider Ltd, which was started by David Tosh and Ben Werdmuller.
In 2010, Curverider was acquired by Thematic Networks and control of the open-source project was turned over to
The Elgg Foundation. Today, Elgg is a community-driven open source project and has a variety of contributors and
supporters.
7.5. History
261