How widgets works internally
ImpressPages CMS requires very little knowledge to start building widgets as minimal widget is just one HTML file (see hello widgets example). But at the same time it scales to any size you need. To write complex widgets, like ones that accept files from users, you need to understand the architecture.
Each widget has a PHP class which is responsible to handle the widget. If you don't put PHP file in widget folder system will pick a default widget class Modules/standard/content_management/Widget which does basic operations.
This class is responsible for following actions:
- generate HTML for widget management
- generate HTML for widget preview
- update widget
- delete widget
- duplicate widget
- other (please read comments in default PHP class file)
If default behavior doesn't work for you, you can put your own class in your widget directory which should be named as your widget (e.g. yourWidgetName.php) and should extend Modules\standard\content_management\Widget.
managementHtml / previewHtml methods
These methods should return HTML for widget management / preview. By default they pass all widget data to view files and that's it. In rare cases you may need to do some calculations or checks before passing data to the view. In this case you can override these methods.
Update method is being called every time when user confirms a widget. This method gets:
- widget ID
- current data that is stored in the database
Method should do all actions required and return new data that needs to be stored. This method should avoid any actions on database. All data that needs to be stored should be returned as array. It will be stored by ImpressPages CMS system. Only files or some other very large or specific data can be stored in file system or somewhere else.
If your widget stores files you need to store each file in repository using method:
$newImageName = \Modules\administrator\repository\Model::addFile('fileNameRelativeToWebsiteRoot', 'module_group/module_name', $widgetId);
This method duplicates file and returns new file name relative to website root. So usually your widget stores uploads to TMP_FILE_DIR and repository will store it in default location. Your widget shouldn't care too much where it is and just store returned value.
'module_group/module_name' is unique identification of your module. It is used to debug repository records and allows to find all records easily in repository that are used by the module. It is just a string which should be the same within boundaries of your widget. Don't try anything creative here. Use values from your plugin install/plugin.ini file. That will assure uniqueness.
$widgetId - tells which widget uses that file.
If user updates / deletes / adds new file, your update method should track that and use repository addFile, unbindFile methods to replicate changes in repository.
You can't just store files without using repository. Read "duplicate method" for the reason.
Each time user edits a widget it is duplicated and only duplicated widget is actually modified by user actions. This is done to enable revisions (page history). If your widget doesn't deal with files default duplicate method is fine. It will duplicate all widget data and create new widget ID. If your widget stores some files you need to inform repository about that.
This method will tell that duplicated widget uses the same file as its predecessor:
\Modules\administrator\repository\Model::bindFile('fileNameRelativeToWebsiteRoot', 'module_group/module_name', $newId);
Repository tracks all file usages and deletes the file only when there are no references to it. If you will skip this file, repository will not know that file is being used by widget duplicate and will delete it as soon as predecessor will be deleted. And sooner or later it is going to happen when old page revisions will be automatically removed by garbage collector.
You can't avoid repository and simply save files in file system. As then on each widget duplication you will need to duplicate file. And that is going to eat disk space very quickly.
Default IpImage and IpFile widgets are good file handling examples. You can find them in ip_cms/modules/standard/content_management/widget
This method should destroy widget. If your widget doesn't deal with files or other external data you don't need to do anything. Widget data will be removed automatically. If your widget has some files you should extend this method and unbind files that are being used using method:
\Modules\administrator\repository\Model::unbindFile('fileNameRelativeToWebsiteRoot', 'module_group/module_name', $widgetId);
This will tell the repository that widget by id $widgetId doesn't use that resource anymore. There could be many widgets pointing to the same file. And usually widget duplicates points to the same files. Repository will keep file until at least one reference to that file exists. Make sure that duplicate method registers new duplicated widgets.
As you can see there is no "create" action. This is to avoid widget reliance on initialization. When user drags new widget it is being initialized with empty dataset by ImpressPages CMS core. When user enters some data and press "confirm", update action is being fired. So your widget should be able to work without initialization.
- g - module group name (see your install/plugin.ini)
- m - module name (see your install/plugin.ini)
- a - 'widgetPost'
- instanceId - you get this variable in managementHtml / previewHtml methods and can pass it to a widget when generating HTML
- any other data you want to post
- $instanceId - widget instance id
- $postData - data that has been posted
- $data - widget data from the database
If you want to examine the code of fully functional widget, download Custom menu widget plugin at https://github.com/impresspages/plugin-menu.