Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_history_f3.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/units/structure/structure_config.php
===================================================================
diff -u -N -r14726 -r14856
--- branches/5.2.x/core/units/structure/structure_config.php (.../structure_config.php) (revision 14726)
+++ branches/5.2.x/core/units/structure/structure_config.php (.../structure_config.php) (revision 14856)
@@ -1,6 +1,6 @@
'SELECT %1$s.* %2$s FROM %1$s',
),
- 'SubItems' => Array('content'),
+ 'SubItems' => Array('content', 'page-revision'),
'ListSortings' => Array(
'' => Array(
@@ -205,6 +205,7 @@
),
'PageCacheKey' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'PageExpiration' => Array ('type' => 'int', 'default' => NULL),
+ 'LiveRevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1),
'DirectLinkEnabled' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/button_vp_left.png
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/install/upgrades.php
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/install/upgrades.php (.../upgrades.php) (revision 14853)
+++ branches/5.2.x/core/install/upgrades.php (.../upgrades.php) (revision 14856)
@@ -1,6 +1,6 @@
transformSortings();
$this->transformFieldPhrases(); // because of "la_col_ItemPrefix" phrase
+ $this->createPageRevisions();
}
}
@@ -1896,4 +1897,31 @@
$this->Conn->Query($sql);
}
}
+
+ protected function createPageRevisions()
+ {
+ $sql = 'SELECT DISTINCT PageId
+ FROM ' . TABLE_PREFIX . 'PageContent';
+ $page_ids = $this->Conn->GetCol($sql);
+
+ foreach ($page_ids as $page_id) {
+ $fields_hash = Array (
+ 'PageId' => $page_id,
+ 'RevisionNumber' => 1,
+ 'IsDraft' => 0,
+ 'FromRevisionNumber' => 0,
+ 'CreatedById' => USER_ROOT,
+ 'CreatedOn' => adodb_mktime(),
+ 'Status' => STATUS_ACTIVE,
+ );
+
+ $this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'PageRevisions');
+
+ $fields_hash = Array (
+ 'RevisionId' => $this->Conn->getInsertID(),
+ );
+
+ $this->Conn->doUpdate($fields_hash, TABLE_PREFIX . 'PageContent', 'PageId = ' . $page_id);
+ }
+ }
}
\ No newline at end of file
Index: branches/5.2.x/core/admin_templates/categories/edit_content.tpl
===================================================================
diff -u -N -r14585 -r14856
--- branches/5.2.x/core/admin_templates/categories/edit_content.tpl (.../edit_content.tpl) (revision 14585)
+++ branches/5.2.x/core/admin_templates/categories/edit_content.tpl (.../edit_content.tpl) (revision 14856)
@@ -16,31 +16,21 @@
'select',
'',
function() {
- submit_event('content','');
+ submit_event('content', 'OnSaveContentBlock');
}
)
);
a_toolbar.AddButton(
new ToolBarButton(
'cancel',
- '',
+ '',
function() {
- cancel_edit('content','OnCancelEdit','','');
+ window_close();
}
)
);
- a_toolbar.AddButton(
- new ToolBarButton(
- 'reset_edit',
- '',
- function() {
- reset_form('content', 'OnReset', '');
- }
- )
- );
-
a_toolbar.Render();
@@ -81,4 +71,35 @@
+
+
\ No newline at end of file
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/close_black.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/install/install_schema.sql
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/install/install_schema.sql (.../install_schema.sql) (revision 14853)
+++ branches/5.2.x/core/install/install_schema.sql (.../install_schema.sql) (revision 14856)
@@ -1,6 +1,6 @@
CREATE TABLE PermissionConfig (
PermissionConfigId int(11) NOT NULL auto_increment,
- PermissionName varchar(30) NOT NULL default '',
+ PermissionName varchar(255) NOT NULL default '',
Description varchar(255) NOT NULL default '',
ModuleId varchar(20) NOT NULL default '0',
IsSystem tinyint(1) NOT NULL DEFAULT '0',
@@ -474,6 +474,7 @@
OverridePageCacheKey tinyint(4) NOT NULL DEFAULT '0',
PageCacheKey varchar(255) NOT NULL DEFAULT '',
PageExpiration int(11) DEFAULT NULL,
+ LiveRevisionNumber int(11) NOT NULL DEFAULT '1',
DirectLinkEnabled tinyint(4) NOT NULL DEFAULT '1',
DirectLinkAuthKey varchar(20) NOT NULL,
PRIMARY KEY (CategoryId),
@@ -503,7 +504,8 @@
KEY EnablePageCache (EnablePageCache),
KEY OverridePageCacheKey (OverridePageCacheKey),
KEY PageExpiration (PageExpiration),
- KEY Protected (Protected)
+ KEY Protected (Protected),
+ KEY LiveRevisionNumber (LiveRevisionNumber)
);
CREATE TABLE CategoryCustomData (
@@ -1025,15 +1027,34 @@
PageContentId int(11) NOT NULL AUTO_INCREMENT,
ContentNum int(11) NOT NULL DEFAULT '0',
PageId int(11) NOT NULL DEFAULT '0',
+ RevisionId int(11) NOT NULL,
l1_Content text,
l2_Content text,
l3_Content text,
l4_Content text,
l5_Content text,
PRIMARY KEY (PageContentId),
- KEY ContentNum (ContentNum,PageId)
+ KEY ContentNum (ContentNum,PageId),
+ KEY RevisionId (RevisionId)
);
+CREATE TABLE PageRevisions (
+ RevisionId int(11) NOT NULL AUTO_INCREMENT,
+ PageId int(11) NOT NULL,
+ RevisionNumber int(11) NOT NULL,
+ IsDraft tinyint(4) NOT NULL,
+ FromRevisionId int(11) NOT NULL,
+ CreatedById int(11) DEFAULT NULL,
+ CreatedOn int(11) DEFAULT NULL,
+ AutoSavedOn int(11) DEFAULT NULL,
+ `Status` tinyint(4) NOT NULL DEFAULT '2',
+ PRIMARY KEY (RevisionId),
+ KEY PageId (PageId),
+ KEY RevisionNumber (RevisionNumber),
+ KEY IsDraft (IsDraft),
+ KEY `Status` (`Status`)
+);
+
CREATE TABLE FormFields (
FormFieldId int(11) NOT NULL AUTO_INCREMENT,
FormId int(11) NOT NULL DEFAULT '0',
Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_preview_f2.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_preview.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_preview_f3.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/admin_templates/js/toolbar.js
===================================================================
diff -u -N -r14244 -r14856
--- branches/5.2.x/core/admin_templates/js/toolbar.js (.../toolbar.js) (revision 14244)
+++ branches/5.2.x/core/admin_templates/js/toolbar.js (.../toolbar.js) (revision 14856)
@@ -18,15 +18,13 @@
}
- if (typeof(onclick) == 'function') {
+ if ( $.isFunction(onclick) ) {
this.onClick = onclick;
}
else {
this.onClick = function() {
- if (eval('typeof('+this.Title+')') == 'function')
- eval(this.Title + '()');
+ this.runFunction(this.Title);
}
-
}
this.imgObject = null;
@@ -59,17 +57,13 @@
ToolBarButton.prototype.IconsPath = function()
{
- if (typeof(img_path) == 'undefined') {
- //alert('error: toolbar image path not set');
- }
-
var $module_path = this.Module;
if (this.Module != 'core') {
$module_path = 'modules/' + $module_path;
}
- return img_path.replace('#MODULE#', $module_path) + 'toolbar/';
+ return this.ToolBar.IconPath.replace('#MODULE#', $module_path) + 'toolbar/';
}
ToolBarButton.prototype.GetHTML = function() {
@@ -150,6 +144,12 @@
};
}
+ToolBarButton.prototype.runFunction = function($name) {
+ if ( window[$name] !== undefined && $.isFunction( window[$name] ) ) {
+ window[$name]();
+ }
+}
+
ToolBarButton.prototype.SetOnClick = function() {
// we have SetOnMouseOut for this ???
/*this.Container.onmouseout = function() {
@@ -162,9 +162,7 @@
if (this.inClick || this.btn.ReadOnly) return;
this.inClick = true;
- if (eval('typeof('+this.btn.Title+')') == 'function') {
- eval(this.btn.Title + '()');
- }
+ this.runFunction(this.btn.Title);
this.inClick = false;
}
@@ -285,13 +283,24 @@
/* ----------- */
-function ToolBar(icon_prefix, $module)
+function ToolBar(icon_prefix, $module, $image_path)
{
this.Module = $module ? $module : 'core';
this.IconPrefix = icon_prefix ? icon_prefix : 'tool_';
this.IconSize = {w:32,h:32};
this.Buttons = {};
this.UseLabels = typeof($use_toolbarlabels) != 'undefined' ? $use_toolbarlabels : false;
+
+ if ( $image_path !== undefined ) {
+ this.IconPath = $image_path;
+ }
+ else if ( typeof(img_path) != 'undefined' ) {
+ this.IconPath = img_path;
+ }
+ else {
+ this.IconPath = '';
+// alert('error: toolbar image path not set');
+ }
}
ToolBar.prototype.AddButton = function(a_button)
Index: branches/5.2.x/core/install/english.lang
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/install/english.lang (.../english.lang) (revision 14853)
+++ branches/5.2.x/core/install/english.lang (.../english.lang) (revision 14856)
@@ -37,6 +37,7 @@
TG9jYXRl
TW92ZSBEb3du
TW92ZSBVcA==
+ UHVibGlzaGluZyBUb29scw==
UmVidWlsZA==
UmVjb21waWxl
UmVmcmVzaA==
@@ -54,6 +55,7 @@
VW5zZWxlY3Q=
VXA=
VXNl
+ Ynk=
Q2FuY2Vs
U2VjdGlvbg==
TnVtYmVyIG9mIGRheXMgZm9yIGEgY2F0LiB0byBiZSBORVc=
@@ -228,7 +230,9 @@
RG93bmxvYWQgQ1NW
RG93bmxvYWQgRXhwb3J0IEZpbGU=
RG93bmxvYWQgTGFuZ3VhZ2UgRXhwb3J0
+ RHJhZnQ=
RHJhZnQgQXZhaWxhYmxl
+ ZHJhZnQgc2F2ZWQgYXQgJXM=
Q29udGVudCBFZGl0b3I=
WW91IGhhdmUgbm90IHNhdmVkIGNoYW5nZXMgdG8gdGhlIGl0ZW0geW91IGFyZSBlZGl0aW5nITxiciAvPkNsaWNrIE9LIHRvIGxvb3NlIGNoYW5nZXMgYW5kIGdvIHRvIHRoZSBzZWxlY3RlZCBzZWN0aW9uPGJyIC8+b3IgQ2FuY2VsIHRvIHN0YXkgaW4gdGhlIGN1cnJlbnQgc2VjdGlvbi4=
RGVmYXVsdCB0ZXh0
@@ -762,6 +766,7 @@
Q3VzdG9tICJUbyIgUmVjaXBpZW50KC1zKQ==
Q3VzdG9tIFNlbmRlcg==
ZGF5KHMp
+ RGVjbGluZWQ=
RGVmYXVsdCBXZWJzaXRlIGFkZHJlc3M=
RGVueQ==
RGVzY3JpcHRpb24=
@@ -801,6 +806,7 @@
UGhvbmU=
UG9wdXAgV2luZG93
UHJvY2Vzc2Vk
+ UHVibGlzaGVk
UXVlcnkgU3RyaW5nIChTSUQp
UmF0aW5n
UmVjaXBpZW50IEUtbWFpbA==
@@ -847,6 +853,9 @@
KEdNVCArMDk6MDAp
UGFkZGluZ3M=
UGFnZQ==
+ QXR0ZW50aW9uOiAlcyBpcyBjdXJyZW50bHkgZWRpdGluZyB0aGlzIHNlY3Rpb24h
+ QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uISA=
+ QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uIQ==
UGFzc3dvcmRzIGRvIG5vdCBtYXRjaA==
UGFzc3dvcmQgaXMgdG9vIHNob3J0LCBwbGVhc2UgZW50ZXIgYXQgbGVhc3QgJXMgY2hhcmFjdGVycw==
UGVuZGluZw==
@@ -1007,8 +1016,10 @@
Tm90IGFsbCByZXF1aXJlZCBmaWVsZHMgYXJlIGZpbGxlZC4gUGxlYXNlIGZpbGwgdGhlbSBmaXJzdC4=
Q29tbWVudHMgcGVyIFBhZ2U=
Q29tbWVudHMgcGVyIFBhZ2UgKHNob3J0LWxpc3Qp
+ UmV2aXNpb24gIyVz
SG9tZQ==
U2FtcGxlIFRleHQ=
+ c2F2ZWQgYXQgJXM=
U2F2ZSBVc2VybmFtZSBvbiBUaGlzIENvbXB1dGVy
U2VhcmNo
QmFzaWMgUGVybWlzc2lvbnM=
@@ -1264,7 +1275,9 @@
RWRpdGluZyBBZ2VudA==
RWRpdGluZyBCYW4gUnVsZQ==
RWRpdGluZyBDaGFuZ2VzIExvZw==
+ Q29udGVudCBFZGl0b3IgLSBBdXRvLXNhdmVkIGF0ICVz
RWRpdGluZyBDb3VudHJ5L1N0YXRl
+ RWRpdGluZyBEcmFmdCAoJTIkcyk=
RWRpdGluZyBFbWFpbCBFdmVudA==
RWRpdGluZyBGaWxl
RWRpdGluZyBNZW1iZXJzaGlw
@@ -1378,6 +1391,7 @@
Vmlld2luZyBmb3JtIHN1Ym1pc3Npb24=
Vmlld2luZyBNYWlsaW5nIExpc3Q=
Vmlld2luZyBSZXBseQ==
+ Vmlld2luZyBSZXZpc2lvbiAjJXMgKCVzKQ==
VmlzaXRz
V2Vic2l0ZQ==
dG8=
@@ -1404,13 +1418,15 @@
RGVueQ==
RGV0YWlscw==
RGlzYWJsZQ==
+ RGlzY2FyZA==
RWRpdA==
RWRpdCBDdXJyZW50IFNlY3Rpb24=
RnJvbnQtRW5kIE9ubHk=
RW5hYmxl
RXhwb3J0
RXhwb3J0IExhbmd1YWdl
SGlkZSBNZW51
+ SGlzdG9yeQ==
SG9tZQ==
SW1wb3J0
SW1wb3J0IExhbmd1YWdl
@@ -1444,9 +1460,11 @@
TmV4dA==
UGFzdGU=
UHJldmlvdXM=
+ UHJldmlldw==
U2V0IFByaW1hcnkgR3JvdXA=
UHJpbnQ=
UHJvY2VzcyBRdWV1ZQ==
+ UHVibGlzaA==
UmVidWlsZCBTZWN0aW9uIENhY2hl
UmVjYWxjdWxhdGUgUHJpb3JpdGllcw==
UmVmcmVzaA==
Index: branches/5.2.x/core/kernel/languages/phrases_cache.php
===================================================================
diff -u -N -r14699 -r14856
--- branches/5.2.x/core/kernel/languages/phrases_cache.php (.../phrases_cache.php) (revision 14699)
+++ branches/5.2.x/core/kernel/languages/phrases_cache.php (.../phrases_cache.php) (revision 14856)
@@ -1,6 +1,6 @@
Application->isAdmin) {
$language_id = $this->Application->Session->GetField('Language');
+ $this->AdminLanguageId = $language_id; // same languages, when used from Admin Console
}
else {
$language_id = $this->Application->GetVar('m_lang');
Index: branches/5.2.x/core/install/install_data.sql
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/install/install_data.sql (.../install_data.sql) (revision 14853)
+++ branches/5.2.x/core/install/install_data.sql (.../install_data.sql) (revision 14856)
@@ -497,6 +497,11 @@
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.DELETE', 'la_PermName_Category.Delete_desc', 'In-Portal', 1);
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.ADD.PENDING', 'la_PermName_Category.AddPending_desc', 'In-Portal', 1);
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.MODIFY', 'la_PermName_Category.Modify_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD', 'la_PermName_Category.Revision.Add_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal', 1);
INSERT INTO PermissionConfig VALUES (DEFAULT, 'ADMIN', 'la_PermName_Admin_desc', 'Admin', 1);
INSERT INTO PermissionConfig VALUES (DEFAULT, 'LOGIN', 'la_PermName_Login_desc', 'Front', 1);
INSERT INTO PermissionConfig VALUES (DEFAULT, 'DEBUG.ITEM', 'la_PermName_Debug.Item_desc', 'Admin', 1);
@@ -552,6 +557,9 @@
INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.ADD.PENDING', 13, 1, 0, 1);
INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.DELETE', 11, 1, 0, 1);
INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.MODIFY', 11, 1, 0, 1);
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.ADD', 11, 1, 0, 1);
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 11, 1, 0, 1);
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 11, 1, 0, 1);
INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:service.view', 11, 1, 1, 0);
INSERT INTO Permissions VALUES (DEFAULT, 'in-portal:service.edit', 11, 1, 1, 0);
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/button_vp_right2.png
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_history.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/units/content/content_eh.php
===================================================================
diff -u -N -r14628 -r14856
--- branches/5.2.x/core/units/content/content_eh.php (.../content_eh.php) (revision 14628)
+++ branches/5.2.x/core/units/content/content_eh.php (.../content_eh.php) (revision 14856)
@@ -1,6 +1,6 @@
finalizePermissionCheck($event, $perm_status);
}
+
+ /**
+ * Saves changes to a content block (+ creates draft if missing)
+ *
+ * @param kEvent $event
+ */
+ function OnSaveContentBlock(&$event)
+ {
+ if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
+ $event->status = erFAIL;
+ return ;
+ }
+
+ if ( !$this->saveContentBlock($event, false) ) {
+ $event->status = erFAIL;
+ }
+
+ $event->SetRedirectParam('opener', 'u');
+ }
+
+ /**
+ * Performs auto-save of current content block (will create draft too)
+ *
+ * @param kEvent $event
+ */
+ function OnAutoSave(&$event)
+ {
+ $event->status = erSTOP;
+
+ if ( $this->Application->GetVar('ajax') != 'yes' ) {
+ return ;
+ }
+
+ echo $this->saveContentBlock($event, true);
+ }
+
+ /**
+ * Saves content block
+ *
+ * @param kEvent $event
+ * @param bool $is_draft
+ * @return string
+ */
+ function saveContentBlock(&$event, $is_draft)
+ {
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+ /* @var $object kDBItem */
+
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if ( !$items_info ) {
+ return ;
+ }
+
+ list ($object, $revision) = $this->getContentBlockAndRevision($event);
+
+ list (, $field_values) = each($items_info);
+ $object->SetFieldsFromHash($field_values);
+ $updated = $object->Update();
+
+ if ( $updated ) {
+ $revision->SetDBField('AutoSavedOn_date', adodb_mktime());
+ $revision->SetDBField('AutoSavedOn_time', adodb_mktime());
+ $revision->Update();
+ }
+
+ if ( $is_draft ) {
+ if ( $updated ) {
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ return $revision->GetField('AutoSavedOn') . ' (' . $page_helper->getAgoTime( $revision->GetDBField('AutoSavedOn') ) . ')';
+ }
+ }
+ else {
+ return $updated;
+ }
+
+ return '';
+ }
+
+ /**
+ * Returns last autosave time
+ *
+ * @param kEvent $event
+ */
+ function OnGetAutoSaveTime(&$event)
+ {
+ $event->status = erSTOP;
+
+ if ( $this->Application->GetVar('ajax') != 'yes' ) {
+ return ;
+ }
+
+ list ($object, $revision) = $this->getContentBlockAndRevision($event);
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ $time = $revision->GetField('AutoSavedOn');
+
+ if ( $time ) {
+ echo $time . ' (' . $page_helper->getAgoTime( $revision->GetDBField('AutoSavedOn') ) . ')';
+ }
+ }
+
+ /**
+ * Loads content block from given revision
+ *
+ * @param kDBItem $object
+ * @param kDBItem $revision
+ */
+ function loadFromRevision(&$object, &$revision)
+ {
+ $load_keys = Array (
+ 'PageId' => $object->GetDBField('PageId'),
+ 'ContentNum' => $object->GetDBField('ContentNum'),
+ 'RevisionId' => $revision->GetID(),
+ );
+
+ $object->Load($load_keys);
+ }
+
+ function getContentBlockAndRevision(&$event)
+ {
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+ /* @var $object kDBItem */
+
+ $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
+ if ( !$items_info ) {
+ return ;
+ }
+
+ list ($id, $field_values) = each($items_info);
+ $object->Load($id);
+
+ $revision =& $this->Application->recallObject('page-revision', null, Array ('skip_autoload' => true));
+ /* @var $revision kDBItem */
+
+ $revision->Load( $object->GetDBField('RevisionId') );
+
+ if ( !$revision->GetDBField('IsDraft') ) {
+ // editing live revision of a page's content block -> get draft for current user and page
+ $load_keys = Array (
+ 'PageId' => $revision->GetDBField('PageId'),
+ 'IsDraft' => 1,
+ 'CreatedById' => $this->Application->RecallVar('user_id'),
+ );
+
+ $revision->Load($load_keys);
+
+ if ( $revision->isLoaded() ) {
+ // draft found -> use draft's content block version
+ $this->loadFromRevision($object, $revision);
+ }
+ else {
+ // draft not found -> create new
+ $revision->SetDBFieldsFromHash($load_keys);
+ $revision->SetDBField('FromRevisionId', $object->GetDBField('RevisionId'));
+
+ if ( $revision->Create() ) {
+ $this->loadFromRevision($object, $revision);
+ }
+ }
+ }
+
+ return Array (&$object, &$revision);
+ }
}
\ No newline at end of file
Index: branches/5.2.x/core/admin_templates/incs/cms.css
===================================================================
diff -u -N -r14244 -r14856
--- branches/5.2.x/core/admin_templates/incs/cms.css (.../cms.css) (revision 14244)
+++ branches/5.2.x/core/admin_templates/incs/cms.css (.../cms.css) (revision 14856)
@@ -175,4 +175,191 @@
display: none;
opacity: 1;
filter: alpha(opacity=100);
-}
\ No newline at end of file
+}
+
+
+/* === Misc Styles for "Content Revision Control" button === */
+.cms-clear { clear: both; }
+.cms-right { float: right; }
+.cms-left { float: left; }
+
+
+/* === Current Revision Information === */
+#cms-current-revision-info {
+ color: #4b4b4b;
+ font-size: 11px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ white-space: nowrap;
+ float: left;
+ padding: 0px 30px;
+}
+
+#cms-current-revision-info span {
+ display: block;
+ /*color: #008c1b;*/
+ font-size: 16px;
+ display: block;
+ padding: 10px 0px 3px 0px;
+}
+
+#cms-current-revision-info span.cms-revision-published { color: #15b300; }
+#cms-current-revision-info span.cms-revision-pending { color: #ff9600; }
+#cms-current-revision-info span.cms-revision-declined { color: #f90000; }
+
+
+/* === Revision Editing Toolbar === */
+#cms-revision-toolbar {
+ background-color: #F0F1EB;
+ border-collapse: collapse;
+ border-color: #aaaaaa;
+ border-style: solid;
+ border-width: 0 1px 1px;
+ font-size: 8pt;
+}
+
+#cms-revision-toolbar-layer {
+ position: absolute;
+ width: 630px;
+ top: 0px;
+ z-index: 100;
+}
+
+.toolbar-button, .toolbar-button-disabled, .toolbar-button-over {
+ color: #006F99;
+ float: left;
+ font-size: 8pt;
+ padding: 5px;
+ text-align: center;
+ vertical-align: middle;
+}
+
+a#cms-toggle-revision-toolbar {
+ display: block;
+ background: url(@templates_base@/img/top_frame/revision_control/button_vp_right.png) top right no-repeat;
+ padding-right: 24px;
+ float: right;
+ margin-top: -1px;
+ text-decoration: none;
+}
+
+a#cms-toggle-revision-toolbar span {
+ display: block;
+ background: url(@templates_base@/img/top_frame/revision_control/button_vp_left.png) top left no-repeat;
+ line-height: 30px;
+ color: #000000;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 13px;
+ font-weight: bold;
+ padding: 0px 10px 0px 15px;
+}
+
+a#cms-toggle-revision-toolbar:hover span {
+ color: #666666;
+}
+
+a#cms-toggle-revision-toolbar.opened {
+ background: url(@templates_base@/img/top_frame/revision_control/button_vp_right2.png) top right no-repeat;
+}
+
+#cms-close-toolbar {
+ float: right;
+ display: block;
+ width: 10px;
+ height: 10px;
+ background: url(@templates_base@/img/top_frame/revision_control/close_black.gif) top left;
+ overflow: hidden;
+ margin: 10px 10px 0 0;
+}
+
+
+/* === Revision Editing Notice === */
+#cms-editing-notice {
+ width: 230px;
+ position: absolute;
+ z-index: 101;
+ margin-top: 50px;
+ margin-left: 10px;
+
+ display: none;
+}
+
+#cms-editing-notice .top {
+ background: url(@templates_base@/img/top_frame/revision_control/message_background_red.png) left top no-repeat;
+ padding: 30px 20px 10px 20px;
+ color: #FFFFFF;
+ font-weight: bold;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+#cms-editing-notice .bottom {
+ background: url(@templates_base@/img/top_frame/revision_control/message_background_red.png) left bottom no-repeat;
+ height: 6px;
+}
+
+#cms-close-editing-notice {
+ float:right;
+ display:block;
+ width:10px;
+ height:10px;
+ background:url(@templates_base@/img/top_frame/revision_control/close_white.gif) top left;
+ overflow:hidden;
+ margin:-5px -10px 0 0;
+}
+
+
+/* === Revision Dropdown === */
+#cms-revision-dropdown {
+ width: 240px;
+ position: absolute;
+ margin-left: 260px;
+ margin-top: 30px;
+ z-index: 102;
+
+ display: none;
+}
+
+#cms-revision-dropdown .top {
+ border-top: 1px solid #aaaaaa;
+ border-left: 1px solid #aaaaaa;
+ border-right: 1px solid #aaaaaa;
+}
+
+#cms-revision-dropdown .item, #cms-revision-dropdown .item:hover {
+ color: #666666;
+ background: url(@templates_base@/img/top_frame/revision_control/history_item_background.gif) top left repeat-x #FFFFFF;
+ padding: 10px 13px;
+ border-top: 1px solid #e7e7e7;
+ font-size: 11px;
+ cursor: pointer;
+}
+
+#cms-revision-dropdown .item:hover {
+ background: url(@templates_base@/img/top_frame/revision_control/history_item_background_hover.gif) top left repeat-x #F8F8F8;
+}
+
+#cms-revision-dropdown .item .red {
+ color: #FF0000;
+ padding-top: 6px;
+}
+
+#cms-revision-dropdown .bottom {
+ background: url(@templates_base@/img/top_frame/revision_control/history_bottom.png) top left no-repeat;
+ height: 6px;
+}
+
+.item .cms-revision-published, .item .cms-revision-pending, .item .cms-revision-declined {
+ display: block;
+ font-size: 13px;
+ font-weight: bold;
+ padding-bottom: 8px;
+}
+
+.item .cms-revision-published, .item .cms-revision-published a { color: #15b300 !important; }
+.item .cms-revision-published a:hover { color: #1ada00 !important; }
+
+.item .cms-revision-pending, .item .cms-revision-pending a { color: #ff9600 !important; }
+.item .cms-revision-pending a:hover { color: #ffba58 !important; }
+
+.item .cms-revision-declined, .item .cms-revision-declined a { color: #f90000 !important; }
+.item .cms-revision-declined a:hover { color: #ff6666 !important; }
\ No newline at end of file
Index: branches/5.2.x/core/units/page_revisions/page_revision_eh.php
===================================================================
diff -u -N
--- branches/5.2.x/core/units/page_revisions/page_revision_eh.php (revision 0)
+++ branches/5.2.x/core/units/page_revisions/page_revision_eh.php (revision 14856)
@@ -0,0 +1,359 @@
+Name == 'OnItemBuild' ) {
+ return true;
+ }
+
+ if ( $event->Name == 'OnGetInfo' || $event->Name == 'OnDiscard' ) {
+ return $this->Application->isAdminUser;
+ }
+
+ $perm_helper =& $this->Application->recallObject('PermissionsHelper');
+ /* @var $perm_helper kPermissionsHelper */
+
+ if ( $event->Name == 'OnSave' ) {
+ $perm_status = $this->Application->CheckPermission('CATEGORY.REVISION.ADD', 0) || $this->Application->CheckPermission('CATEGORY.REVISION.ADD.PENDING', 0);
+
+ return $perm_helper->finalizePermissionCheck($event, $perm_status);
+ }
+
+ if ( $event->Name == 'OnPublish' || $event->Name == 'OnDecline' ) {
+ $perm_status = $this->Application->CheckPermission('CATEGORY.REVISION.MODERATE', 0);
+
+ return $perm_helper->finalizePermissionCheck($event, $perm_status);
+ }
+
+ return parent::CheckPermission($event);
+ }
+
+ /**
+ * Lists all current page revisions
+ *
+ * @param kEvent $event
+ */
+ function SetCustomQuery(&$event)
+ {
+ parent::SetCustomQuery($event);
+
+ $object =& $event->getObject();
+ /* @var $object kDBList */
+
+ $page_id = $event->getEventParam('page_id');
+
+ if ( $this->Application->isAdmin ) {
+ $user_id = $this->Application->RecallVar('user_id');
+ }
+ else {
+ $user_id = $this->Application->RecallVar('admin_user_id');
+ }
+
+ $object->addFilter('draft_filter', 'IF(%1$s.IsDraft = 1, %1$s.CreatedById = ' . $user_id . ', TRUE)');
+
+ if ( $page_id !== false ) {
+ $object->addFilter('parent_filter', '%1$s.PageId = ' . $page_id);
+ }
+ }
+
+ /**
+ * Returns current page revision
+ *
+ * @param kEvent $event
+ */
+ function getPassedID(&$event)
+ {
+ if ( $event->Special == 'current' ) {
+ $page =& $this->Application->recallObject('st.-virtual');
+ /* @var $page kDBItem */
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ $page_id = $page->GetID();
+ $revision_clause = $page_helper->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'));
+
+ $sql = 'SELECT RevisionId
+ FROM ' . TABLE_PREFIX . 'PageRevisions
+ WHERE (PageId = ' . $page_id . ') AND (' . $revision_clause . ')
+ ORDER BY IsDraft DESC, RevisionNumber DESC';
+ $id = $this->Conn->GetOne($sql);
+
+ if ( $id ) {
+ return $id;
+ }
+
+ // no revisions -> create live revision
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ $object->SetDBField('PageId', $page_id);
+ $object->SetDBField('RevisionNumber', 1);
+ $object->SetDBField('Status', STATUS_ACTIVE);
+ $object->Create();
+
+ return $object->GetID();
+ }
+
+ return parent::getPassedID($event);
+ }
+
+ /**
+ * Remembers, who created revision
+ *
+ * @param kEvent $event
+ */
+ function OnBeforeItemCreate(&$event)
+ {
+ parent::OnBeforeItemCreate($event);
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( $this->Application->isAdmin ) {
+ $object->SetDBField('CreatedById', $this->Application->RecallVar('user_id'));
+ }
+ else {
+ $object->SetDBField('CreatedById', $this->Application->RecallVar('admin_user_id'));
+ }
+ }
+
+ /**
+ * Updates revision creation time
+ *
+ * @param kEvent $event
+ */
+ function OnBeforeItemUpdate(&$event)
+ {
+ parent::OnBeforeItemUpdate($event);
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( $object->GetDBField('IsDraft') == 0 && $object->GetOriginalField('IsDraft') == 1 ) {
+ $object->SetDBField('CreatedOn_date', adodb_mktime());
+ $object->SetDBField('CreatedOn_time', adodb_mktime());
+ }
+ }
+
+ /**
+ * Creates new content blocks based on source revision
+ *
+ * @param kEvent $event
+ */
+ function OnAfterItemCreate(&$event)
+ {
+ parent::OnAfterItemCreate($event);
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ if ( !$object->GetDBField('FromRevisionId') ) {
+ return ;
+ }
+
+ $content =& $this->Application->recallObject('content.-item', null, Array ('skip_autoload' => true));
+ /* @var $content kDBItem */
+
+ $sql = $content->GetSelectSQL() . '
+ WHERE pr.RevisionId = ' . $object->GetDBField('FromRevisionId');
+ $content_blocks = $this->Conn->Query($sql);
+
+ foreach ($content_blocks as $content_block) {
+ $content->LoadFromHash($content_block);
+ $content->SetDBField('RevisionId', $object->GetID());
+ $content->Create();
+ }
+ }
+
+ /**
+ * Mark revision as current, once it's approved
+ *
+ * @param kEvent $event
+ */
+ function OnAfterItemUpdate(&$event)
+ {
+ parent::OnAfterItemUpdate($event);
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ $status = $object->GetDBField('Status');
+
+ if ( $status != $object->GetOriginalField('Status') && $status == STATUS_ACTIVE ) {
+ $page =& $this->Application->recallObject('c.revision', null, Array ('skip_autoload' => true));
+ /* @var $page kDBItem */
+
+ $page->Load( $object->GetDBField('PageId') );
+ $page->SetDBField('LiveRevisionNumber', $object->GetDBField('RevisionNumber'));
+ $page->Update();
+ }
+ }
+
+ /**
+ * Returns user, who are edting current page right now
+ *
+ * @param kEvent $event
+ */
+ function OnGetInfo(&$event)
+ {
+ $event->status = erSTOP;
+
+ if ( $this->Application->GetVar('ajax') != 'yes' ) {
+ return ;
+ }
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ $page_id = $this->Application->GetVar('m_cat_id');
+ echo json_encode( $page_helper->getPageInfo($page_id) );
+ }
+
+ /**
+ * Saves user draft into live revision
+ *
+ * @param kEvent $event
+ */
+ function OnSave(&$event)
+ {
+ $revision_id = $this->getCurrentDraftRevision($event);
+
+ if ( $revision_id ) {
+ $object =& $event->getObject( Array('skip_autoload' => true) );
+ /* @var $object kDBItem */
+
+ $object->Load($revision_id);
+ $object->SetDBField('IsDraft', 0);
+ $object->SetDBField('RevisionNumber', $this->getNextAvailableRevision($event));
+
+ if ( $this->Application->CheckPermission('CATEGORY.REVISION.ADD', 0) ) {
+ $object->SetDBField('Status', STATUS_ACTIVE);
+ }
+ elseif ( $this->Application->CheckPermission('CATEGORY.REVISION.ADD.PENDING', 0) ) {
+ $object->SetDBField('Status', STATUS_PENDING);
+ }
+
+ $object->Update();
+ }
+
+ $event->SetRedirectParam('opener', 'u');
+ }
+
+ /**
+ * Discards user draft
+ *
+ * @param kEvent $event
+ */
+ function OnDiscard(&$event)
+ {
+ $revision_id = $this->getCurrentDraftRevision($event);
+
+ if ( $revision_id ) {
+ $temp_handler =& $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
+ /* @var $temp_handler kTempTablesHandler */
+
+ $temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($revision_id));
+ }
+
+ $event->SetRedirectParam('opener', 'u');
+ }
+
+ /**
+ * Makes revision live
+ *
+ * @param kEvent $event
+ */
+ function OnPublish(&$event)
+ {
+ $revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $revision kDBItem */
+
+ if ( !$revision->isLoaded() || $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
+ return ;
+ }
+
+ $revision->SetDBField('Status', STATUS_ACTIVE);
+ $revision->Update();
+
+ $event->SetRedirectParam('opener', 'u');
+ }
+
+ /**
+ * Denies changes made in revision
+ *
+ * @param kEvent $event
+ */
+ function OnDecline(&$event)
+ {
+ $revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $revision kDBItem */
+
+ if ( !$revision->isLoaded() || $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
+ return ;
+ }
+
+ $revision->SetDBField('Status', STATUS_DISABLED);
+ $revision->Update();
+
+ $event->SetRedirectParam('opener', 'u');
+ }
+
+ /**
+ * Returns revision id of user's draft
+ *
+ * @param kEvent $event
+ * @return int
+ */
+ function getCurrentDraftRevision(&$event)
+ {
+ $where_clause = Array (
+ 'IsDraft = 1',
+ 'PageId = ' . $this->Application->GetVar('m_cat_id'),
+ 'CreatedById = ' . $this->Application->RecallVar('user_id'),
+ );
+
+ $sql = 'SELECT ' . $this->Application->getUnitOption($event->Prefix, 'IDField') . '
+ FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
+ WHERE (' . implode(') AND (', $where_clause) . ')';
+
+ return $this->Conn->GetOne($sql);
+ }
+
+ /**
+ * Returns next available revision number for current page
+ *
+ * @param kEvent $event
+ * @return int
+ */
+ function getNextAvailableRevision(&$event)
+ {
+ $sql = 'SELECT MAX(RevisionNumber)
+ FROM ' . $this->Application->getUnitOption($event->Prefix, 'TableName') . '
+ WHERE PageId = ' . $this->Application->GetVar('m_cat_id');
+ $max_revision = (int)$this->Conn->GetOne($sql);
+
+ return $max_revision + 1;
+ }
+}
Index: branches/5.2.x/core/install/remove_schema.sql
===================================================================
diff -u -N -r14789 -r14856
--- branches/5.2.x/core/install/remove_schema.sql (.../remove_schema.sql) (revision 14789)
+++ branches/5.2.x/core/install/remove_schema.sql (.../remove_schema.sql) (revision 14856)
@@ -62,6 +62,7 @@
DROP TABLE StopWords;
DROP TABLE MailingLists;
DROP TABLE PageContent;
+DROP TABLE PageRevisions;
DROP TABLE FormFields;
DROP TABLE FormSubmissions;
DROP TABLE SubmissionLog;
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/history_bottom.png
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/install/upgrades.sql
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/install/upgrades.sql (.../upgrades.sql) (revision 14853)
+++ branches/5.2.x/core/install/upgrades.sql (.../upgrades.sql) (revision 14856)
@@ -2340,4 +2340,41 @@
UPDATE Phrase
SET l<%PRIMARY_LANGUAGE%>_ColumnTranslation = l<%PRIMARY_LANGUAGE%>_Translation
-WHERE PhraseKey IN ('LA_FLD_CATEGORY', 'LA_FLD_ORDER');
\ No newline at end of file
+WHERE PhraseKey IN ('LA_FLD_CATEGORY', 'LA_FLD_ORDER');
+
+CREATE TABLE PageRevisions (
+ RevisionId int(11) NOT NULL AUTO_INCREMENT,
+ PageId int(11) NOT NULL,
+ RevisionNumber int(11) NOT NULL,
+ IsDraft tinyint(4) NOT NULL,
+ FromRevisionId int(11) NOT NULL,
+ CreatedById int(11) DEFAULT NULL,
+ CreatedOn int(11) DEFAULT NULL,
+ AutoSavedOn int(11) DEFAULT NULL,
+ `Status` tinyint(4) NOT NULL DEFAULT '2',
+ PRIMARY KEY (RevisionId),
+ KEY PageId (PageId),
+ KEY RevisionNumber (RevisionNumber),
+ KEY IsDraft (IsDraft),
+ KEY `Status` (`Status`)
+);
+
+ALTER TABLE Category
+ ADD LiveRevisionNumber INT NOT NULL DEFAULT '1' AFTER PageExpiration,
+ ADD INDEX (LiveRevisionNumber);
+
+ALTER TABLE PageContent
+ ADD RevisionId INT NOT NULL AFTER PageId,
+ ADD INDEX (RevisionId);
+
+ALTER TABLE PermissionConfig CHANGE PermissionName PermissionName VARCHAR(255) NOT NULL DEFAULT '';
+
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD', 'la_PermName_Category.Revision.Add_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal', 1);
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal', 1);
+
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.ADD', 11, 1, 0, 1);
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 11, 1, 0, 1);
+INSERT INTO Permissions VALUES(DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 11, 1, 0, 1);
\ No newline at end of file
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/message_background_red.png
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/units/helpers/helpers_config.php
===================================================================
diff -u -N -r14758 -r14856
--- branches/5.2.x/core/units/helpers/helpers_config.php (.../helpers_config.php) (revision 14758)
+++ branches/5.2.x/core/units/helpers/helpers_config.php (.../helpers_config.php) (revision 14856)
@@ -1,6 +1,6 @@
'SiteHelper', 'class' => 'SiteHelper', 'file' => 'site_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
Array ('pseudo' => 'DeploymentHelper', 'class' => 'DeploymentHelper', 'file' => 'deployment_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
+ Array ('pseudo' => 'PageHelper', 'class' => 'PageHelper', 'file' => 'page_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
Array ('pseudo' => 'BackupHelper', 'class' => 'BackupHelper', 'file' => 'backup_helper.php', 'build_event' => ''),
Array ('pseudo' => 'AjaxFormHelper', 'class' => 'AjaxFormHelper', 'file' => 'ajax_form_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
),
Index: branches/5.2.x/core/units/page_revisions/page_revisions_config.php
===================================================================
diff -u -N
--- branches/5.2.x/core/units/page_revisions/page_revisions_config.php (revision 0)
+++ branches/5.2.x/core/units/page_revisions/page_revisions_config.php (revision 14856)
@@ -0,0 +1,93 @@
+ 'page-revision',
+ 'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
+ 'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
+ 'EventHandlerClass' => Array ('class' => 'PageRevisionEventHandler', 'file' => 'page_revision_eh.php', 'build_event' => 'OnBuild'),
+ 'TagProcessorClass' => Array ('class' => 'PageRevisionTagProcessor', 'file' => 'page_revision_tp.php', 'build_event' => 'OnBuild'),
+ 'AutoLoad' => true,
+ 'QueryString' => Array (
+ 1 => 'id',
+ 2 => 'Page',
+ 3 => 'PerPage',
+ 4 => 'event',
+ 5 => 'mode', // needed?
+ ),
+
+ 'IDField' => 'RevisionId',
+ 'ParentTableKey' => 'CategoryId', // linked field in master table
+ 'ForeignKey' => 'PageId', // linked field in subtable
+ 'ParentPrefix' => 'c',
+ 'AutoDelete' => true,
+ 'AutoClone' => true,
+
+ 'TitleField' => 'RevisionNumber',
+
+ 'TableName' => TABLE_PREFIX . 'PageRevisions',
+
+ 'ListSQLs' => Array (
+ '' => ' SELECT %1$s.* %2$s
+ FROM %1$s
+ JOIN ' . TABLE_PREFIX . '%3$sCategory c ON c.CategoryId = %1$s.PageId
+ LEFT JOIN ' . TABLE_PREFIX . 'PortalUser created_by ON created_by.PortalUserId = %1$s.CreatedById'
+ ),
+
+ 'SubItems' => Array ('content'),
+
+ 'ListSortings' => Array (
+ '' => Array (
+ 'Sorting' => Array ('IsDraft' => 'desc', 'RevisionNumber' => 'desc'),
+ )
+ ),
+
+ 'CalculatedFields' => Array (
+ '' => Array (
+ 'CreatedBy' => 'created_by.Username',
+ 'IsLive' => 'IF(%1$s.RevisionNumber = c.LiveRevisionNumber, 1, 0)',
+ ),
+ ),
+
+ 'Fields' => Array (
+ 'RevisionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
+ 'PageId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
+ 'RevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
+ 'IsDraft' => Array (
+ 'type' => 'int',
+ 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
+ 'not_null' => 1, 'default' => 0
+ ),
+ 'FromRevisionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
+ 'CreatedById' => Array (
+ 'type' => 'int',
+ 'formatter' => 'kLEFTFormatter', 'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'), 'left_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'PortalUser WHERE `%s` = \'%s\'', 'left_key_field' => 'PortalUserId', 'left_title_field' => 'Username', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'sample_value' => 'Guest', 'required' => 1,
+ 'default' => NULL
+ ),
+ 'CreatedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
+ 'AutoSavedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
+ 'Status' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (2 => 'la_Pending', 1 => 'la_opt_Published', 0 => 'la_opt_Declined'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2)
+ ),
+
+ 'VirtualFields' => Array (
+ 'CreatedBy' => Array ('type' => 'string', 'default' => ''),
+ 'IsLive' => Array (
+ 'type' => 'int',
+ 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
+ 'default' => 0,
+ )
+ ),
+ );
Index: branches/5.2.x/core/units/categories/categories_config.php
===================================================================
diff -u -N -r14726 -r14856
--- branches/5.2.x/core/units/categories/categories_config.php (.../categories_config.php) (revision 14726)
+++ branches/5.2.x/core/units/categories/categories_config.php (.../categories_config.php) (revision 14856)
@@ -1,6 +1,6 @@
'SELECT %1$s.* %2$s FROM %1$s',
),
- 'SubItems' => Array ('c-rel', 'c-search','c-img', 'c-cdata', 'c-perm', 'content'),
+ 'SubItems' => Array ('c-rel', 'c-search','c-img', 'c-cdata', 'c-perm', 'content', 'page-revision'),
'ListSortings' => Array (
'' => Array (
@@ -401,6 +401,7 @@
),
'PageCacheKey' => Array ('type' => 'string', 'max_len' => 255, 'not_null' => 1, 'default' => ''),
'PageExpiration' => Array ('type' => 'int', 'default' => NULL),
+ 'LiveRevisionNumber' => Array ('type' => 'int', 'not_null' => 1, 'default' => 1),
'DirectLinkEnabled' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
Index: branches/5.2.x/core/units/helpers/permissions_helper.php
===================================================================
diff -u -N -r14731 -r14856
--- branches/5.2.x/core/units/helpers/permissions_helper.php (.../permissions_helper.php) (revision 14731)
+++ branches/5.2.x/core/units/helpers/permissions_helper.php (.../permissions_helper.php) (revision 14856)
@@ -1,6 +1,6 @@
CheckPermission($permission, $is_system, $perm_category) && $owner_checked;
+ $permissions = explode(',', $permission_group);
+
+ if ( $check_admin ) {
+ foreach ($permissions as $permission) {
+ $owner_checked = (strpos($permission, '.OWNER.') !== false) ? $is_owner : true;
+ $has_permission = $has_permission && $this->CheckAdminPermission($permission, $is_system, $perm_category) && $owner_checked;
+ }
}
+ else {
+ foreach ($permissions as $permission) {
+ $owner_checked = (strpos($permission, '.OWNER.') !== false) ? $is_owner : true;
+ $has_permission = $has_permission && $this->CheckPermission($permission, $is_system, $perm_category) && $owner_checked;
+ }
+ }
+
$group_has_permission = $group_has_permission || $has_permission;
if ($group_has_permission) {
@@ -543,8 +554,28 @@
return $this->CheckUserPermission($user_id, $name, $type, $cat_id);
}
+ /**
+ * Check current admin permissions (when called from Front-End) based on it's group permissions in specified category (for non-system permissions) or just checks if system permission is set
+ *
+ * @param string $name permission name
+ * @param int $cat_id category id, current used if not specified
+ * @param int $type permission type {1 - system, 0 - per category}
+ * @return int
+ */
+ function CheckAdminPermission($name, $type = 1, $cat_id = null)
+ {
+ if ( $this->Application->isAdmin ) {
+ return $this->CheckPermission($name, $type, $cat_id);
+ }
+
+ $user_id = $this->Application->RecallVar('admin_user_id');
+ return $this->CheckUserPermission($user_id, $name, $type, $cat_id);
+ }
+
function CheckUserPermission($user_id, $name, $type = 1, $cat_id = null)
{
+ $user_id = (int)$user_id;
+
if ( $user_id == USER_ROOT ) {
// "root" is allowed anywhere
return substr($name, -5) == '.deny' || $name == 'SYSTEM_ACCESS.READONLY' ? 0 : 1;
@@ -565,18 +596,28 @@
// perm cache is build only based on records in db, that's why if permission is not explicitly denied, then
// that (perm cache creator) code thinks that it is allowed & adds corresponding record and code below will
// return incorrect results
-
if ( $user_id == $this->Application->RecallVar('user_id') ) {
- $groups = explode(',', $this->Application->RecallVar('UserGroups'));
+ $groups = $this->Application->RecallVar('UserGroups');
}
- else { // checking not current user
- $sql = 'SELECT GroupId
- FROM ' . TABLE_PREFIX . 'UserGroup
- WHERE (PortalUserId = ' . $user_id . ') AND ( (MembershipExpires IS NULL) OR ( MembershipExpires >= UNIX_TIMESTAMP() ) )';
- $groups = $this->Conn->GetCol($sql);
- array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup'));
+ else {
+ // checking not current user
+ $groups = $this->Application->RecallVar('UserGroups:' . $user_id);
+
+ if ( $groups === false ) {
+// die('me');
+ $sql = 'SELECT GroupId
+ FROM '.TABLE_PREFIX.'UserGroup
+ WHERE (PortalUserId = '.$user_id.') AND ( (MembershipExpires IS NULL) OR ( MembershipExpires >= UNIX_TIMESTAMP() ) )';
+ $groups = $this->Conn->GetCol($sql);
+
+ array_push($groups, $this->Application->ConfigValue('User_LoggedInGroup') );
+ $groups = implode(',', $groups);
+
+ $this->Application->StoreVar('UserGroups:' . $user_id, $groups);
+ }
}
+ $groups = explode(',', $groups);
$cache_key = $name . '|' . $type . '|' . $cat_id . '|' . implode(',', $groups);
$perm_value = $this->Application->getCache('permissions[%' . ($type == 1 ? 'G' : 'C') . 'PermSerial%]:' . $cache_key);
Index: branches/5.2.x/core/units/helpers/page_helper.php
===================================================================
diff -u -N
--- branches/5.2.x/core/units/helpers/page_helper.php (revision 0)
+++ branches/5.2.x/core/units/helpers/page_helper.php (revision 14856)
@@ -0,0 +1,315 @@
+getHistoryPermissionAndUser($page_id);
+
+ $where_clause = Array (
+ 'pr.PageId = ' . $page_id,
+ 'pr.CreatedById <> ' . $user_id,
+ 'pr.IsDraft = 1',
+ );
+
+ $sql = 'SELECT CASE pr.CreatedById WHEN ' . USER_ROOT . ' THEN "root" WHEN ' . USER_GUEST . ' THEN "Guest" ELSE u.Username END
+ FROM ' . $this->Application->getUnitOption('page-revision', 'TableName') . ' pr
+ LEFT JOIN ' . TABLE_PREFIX . 'PortalUser u ON u.PortalUserId = pr.CreatedById
+ WHERE (' . implode(') AND (', $where_clause) . ')';
+ $users = $this->Conn->GetCol($sql);
+
+ $page_revisions = Array ();
+
+ if ( $history_permission ) {
+ $tag_params = Array ('per_page' => -1, 'skip_parent_filter' => 1, 'requery' => 1, 'page_id' => $page_id);
+
+ $revisions =& $this->Application->recallObject('page-revision.list', 'page-revision_List', $tag_params);
+ /* @var $revisions kDBList */
+
+ $revisions->Query();
+ $revisions->GoFirst();
+
+ $status_options = $revisions->GetFieldOptions('Status');
+ $draft_label = $this->Application->Phrase('la_Draft', false, true);
+ $title_label = $this->Application->Phrase('la_RevisionNumber', false, true);
+ $by_label = $this->Application->Phrase('la_By', false, true);
+
+ while ( !$revisions->EOL() ) {
+ $status = $revisions->GetDBField('Status');
+ $status_label = $this->Application->Phrase($status_options['options'][$status], false, true);
+
+ $page_revisions[ 'r' . $revisions->GetDBField('RevisionNumber') ] = Array (
+ 'title' => $revisions->GetDBField('IsDraft') ? $draft_label : sprintf($title_label, $revisions->GetDBField('RevisionNumber')),
+ 'status' => $status,
+ 'status_label' => mb_strtolower($status_label),
+ 'datetime' => $revisions->GetField('CreatedOn'),
+ 'author' => $by_label . ': ' . $revisions->GetField('CreatedById'),
+ 'draft' => (int)$revisions->GetDBField('IsDraft'),
+ );
+
+ $revisions->GoNext();
+ }
+ }
+
+ $current_revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $current_revision kDBItem */
+
+ $revision_status = $current_revision->GetDBField('Status');
+ $status_options = $current_revision->GetFieldOptions('Status');
+ $status_label = $this->Application->Phrase($status_options['options'][$revision_status], false, true);
+
+ $revision_phase = $current_revision->GetDBField('IsDraft') ? 'la_title_EditingDraft' : 'la_title_ViewingRevision';
+ $revision_title = sprintf($this->Application->Phrase($revision_phase, false, true), $current_revision->GetDBField('RevisionNumber'), mb_strtolower($status_label));
+ $current_revision_info = Array ('title' => $revision_title, 'status' => $revision_status, 'saved' => '');
+
+ $autosave_time = $current_revision->GetDBField('AutoSavedOn');
+
+ if ( $autosave_time ) {
+ $phrase = $this->Application->Phrase($current_revision->GetDBField('IsDraft') ? 'la_DraftSavedAt' : 'la_SavedAt', false, true);
+ $current_revision_info['saved'] = sprintf($phrase, $current_revision->GetField('AutoSavedOn_time') . ' (' . $this->getAgoTime($autosave_time) . ')');
+ }
+
+ $currently_editing = $this->getPluralPhrase(
+ count($users),
+ Array (
+ 'phrase1' => 'la_PageCurrentlyEditing1',
+ 'phrase2' => 'la_PageCurrentlyEditing2',
+ 'phrase5' => 'la_PageCurrentlyEditing5',
+ ),
+ false, true
+ );
+
+ $currently_editing = sprintf($currently_editing, implode(', ', $users));
+
+ return Array ('current_revision' => $current_revision_info, 'editors' => $users, 'editors_warning' => $currently_editing, 'revisions' => $page_revisions);
+ }
+
+ /**
+ * Returns time passed between 2 given dates in "X minutes Y seconds ago" format
+ *
+ * @param int $from_date
+ * @param int $to_date
+ * @return string
+ */
+ function getAgoTime($from_date, $to_date = null, $max_levels = 1)
+ {
+ $blocks = Array (
+ Array ('name' => 'year', 'amount' => 60*60*24*365),
+ Array ('name' => 'month' ,'amount' => 60*60*24*31),
+ Array ('name' => 'week', 'amount' => 60*60*24*7),
+ Array ('name' => 'day', 'amount' => 60*60*24),
+ Array ('name' => 'hour', 'amount' => 60*60),
+ Array ('name' => 'minute', 'amount' => 60),
+ Array ('name' => 'second', 'amount' => 1),
+ );
+
+ if ( !isset($to_date) ) {
+ $to_date = adodb_mktime();
+ }
+
+ $diff = abs($to_date - $from_date);
+
+ if ( $diff == 0 ) {
+ return 'now';
+ }
+
+ $current_level = 1;
+ $result = Array ();
+
+ foreach ($blocks as $block) {
+ if ($current_level > $max_levels) {
+ break;
+ }
+
+ if ( $diff / $block['amount'] >= 1 ) {
+ $amount = floor($diff / $block['amount']);
+ $plural = $amount > 1 ? 's' : '';
+
+ $result[] = $amount . ' ' . $block['name'] . $plural;
+ $diff -= $amount * $block['amount'];
+ $current_level++;
+ }
+ }
+
+ return implode(' ', $result) . ' ago';
+ }
+
+ /**
+ * Returns where clause for loading correct revision for a given page
+ *
+ * @param int $page_id
+ * @param int $live_revision_number
+ * @param string $table_name
+ * @return string
+ */
+ function getRevsionWhereClause($page_id, $live_revision_number, $table_name = '')
+ {
+ $revision = (int)$this->Application->GetVar('revision');
+ list ($user_id, $has_permission) = $this->getHistoryPermissionAndUser($page_id);
+
+ if ( $has_permission && $revision ) {
+ $revision_clause = $table_name . 'RevisionNumber = ' . $revision . ' AND ' . $table_name . 'IsDraft = 0';
+ }
+ else {
+ $editing_mode = $this->Application->GetVar('editing_mode'); // not in a EDITING_MODE constant, while in admin console
+ $revision_clause = $table_name . 'RevisionNumber = ' . $live_revision_number . ' AND ' . $table_name . 'IsDraft = 0';
+
+ if ( $this->Application->GetVar('preview') || $editing_mode == EDITING_MODE_CONTENT ) {
+ $revision_clause = '(' . $table_name . 'CreatedById = ' . $user_id . ' AND ' . $table_name . 'IsDraft = 1) OR (' . $revision_clause . ')';
+ }
+ }
+
+ return $revision_clause;
+ }
+
+ /**
+ * Returns current admin user id (even, when called from front-end) and it's revision history view permission
+ *
+ * @param int $page_id
+ * @return Array
+ */
+ function getHistoryPermissionAndUser($page_id)
+ {
+ $user_id = (int)$this->Application->RecallVar($this->Application->isAdmin ? 'user_id' : 'admin_user_id');
+ $history_permission = $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0, $page_id);
+
+ return Array ($user_id, $history_permission);
+ }
+
+ /**
+ * Creates new content block in every revision that misses it. Plus creates first page revision
+ *
+ * @param int $page_id
+ * @param int $num
+ */
+ function createNewContentBlock($page_id, $num)
+ {
+ $sql = 'SELECT pc.PageContentId, pr.RevisionId
+ FROM ' . TABLE_PREFIX . 'PageRevisions pr
+ LEFT JOIN ' . TABLE_PREFIX . 'PageContent pc ON pc.RevisionId = pr.RevisionId AND pc.ContentNum = ' . $num . '
+ WHERE pr.PageId = ' . $page_id;
+ $revisions = $this->Conn->GetCol($sql, 'RevisionId');
+
+ if ( !$revisions ) {
+ // no revisions for a page -> create a live revision
+ $revision =& $this->Application->recallObject('page-revision.live', null, Array ('skip_autoload' => true));
+ /* @var $revision kDBItem */
+
+ $revision->SetDBField('PageId', $page_id);
+ $revision->SetDBField('RevisionNumber', 1);
+ $revision->SetDBField('Status', STATUS_ACTIVE);
+ $revision->Create();
+
+ $revisions[ $revision->GetID() ] = NULL;
+ }
+
+ $content_block =& $this->Application->recallObject('content.new', null, Array ('skip_autoload' => true));
+ /* @var $content_block kDBItem */
+
+ $content_block->SetDBField('PageId', $page_id);
+ $content_block->SetDBField('ContentNum', $num);
+
+ foreach ($revisions as $revision_id => $content_block_id) {
+ if ( is_numeric($content_block_id) ) {
+ continue;
+ }
+
+ $content_block->SetDBField('RevisionId', $revision_id);
+ $content_block->Create();
+ }
+ }
+
+ /**
+ * Loads content block by it's number
+ *
+ * @param kDBItem $content_block
+ * @param CategoriesItem $page
+ * @param int $num
+ *
+ * @return bool
+ */
+ function loadContentBlock(&$content_block, &$page, $num)
+ {
+ $page_id = $page->GetID();
+
+ if ( !EDITING_MODE && !$this->Application->GetVar('preview') ) {
+ $revision_clause = 'pr.RevisionNumber = ' . $page->GetDBField('LiveRevisionNumber') . ' AND pr.IsDraft = 0';
+ }
+ else {
+ $revision_clause = $this->getRevsionWhereClause($page_id, $page->GetDBField('LiveRevisionNumber'), 'pr.');
+ }
+
+
+ $sql = $content_block->GetSelectSQL() . '
+ WHERE (' . $content_block->TableName . '.PageId = ' . $page_id . ') AND (' . $content_block->TableName . '.ContentNum = ' . $num . ') AND (' . $revision_clause . ')
+ ORDER BY pr.IsDraft DESC, pr.RevisionNumber DESC';
+ $content_data = $this->Conn->GetRow($sql);
+
+ $content_block->LoadFromHash($content_data);
+
+ return $content_block->isLoaded();
+ }
+
+ /**
+ * Returns phrase based on given number
+ *
+ * @param int $number
+ * @param Array $forms
+ * @return string
+ */
+ function getPluralPhrase($number, $forms, $allow_editing = true, $use_admin = false)
+ {
+ // normalize given forms
+ if ( !array_key_exists('phrase5', $forms) ) {
+ $forms['phrase5'] = $forms['phrase2'];
+ }
+
+ $phrase_type = $this->getPluralPhraseType($number);
+
+ return $this->Application->Phrase( $forms['phrase' . $phrase_type], $allow_editing, $use_admin );
+ }
+
+ /**
+ * Returns phrase type based on given number
+ *
+ * @param int $number
+ * @return int
+ */
+ function getPluralPhraseType($number)
+ {
+ $last_digit = substr($number, -1);
+ $last_but_one_digit = strlen($number) > 1 ? substr($number, -2, 1) : false;
+ $phrase_type = '5';
+
+ if ($last_but_one_digit != 1) {
+ if ($last_digit == 1) {
+ $phrase_type = '1';
+ }
+ elseif ($last_digit >= 2 && $last_digit <= 4) {
+ $phrase_type = '2';
+ }
+ }
+
+ return $phrase_type;
+ }
+}
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/close_white.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/units/helpers/minifiers/minify_helper.php
===================================================================
diff -u -N -r14837 -r14856
--- branches/5.2.x/core/units/helpers/minifiers/minify_helper.php (.../minify_helper.php) (revision 14837)
+++ branches/5.2.x/core/units/helpers/minifiers/minify_helper.php (.../minify_helper.php) (revision 14856)
@@ -1,6 +1,6 @@
Application->ProcessParsedTag('m', 'TemplatesBase', Array ());
+ if ( isset($params['templates_base']) ) {
+ $templates_base = $params['templates_base'];
+ }
+ else {
+ $templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ());
+ }
+
$templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base);
$string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string);
Index: branches/5.2.x/core/units/page_revisions/page_revision_tp.php
===================================================================
diff -u -N
--- branches/5.2.x/core/units/page_revisions/page_revision_tp.php (revision 0)
+++ branches/5.2.x/core/units/page_revisions/page_revision_tp.php (revision 14856)
@@ -0,0 +1,29 @@
+getObject($params);
+ /* @var $object kDBItem */
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ return $page_helper->getAgoTime( $object->GetDBField('AutoSavedOn') );
+ }
+}
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/button_vp_right.png
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/kernel/constants.php
===================================================================
diff -u -N -r14853 -r14856
--- branches/5.2.x/core/kernel/constants.php (.../constants.php) (revision 14853)
+++ branches/5.2.x/core/kernel/constants.php (.../constants.php) (revision 14856)
@@ -1,6 +1,6 @@
GetVar('admin') ) {
- // viewing front-end through admin's frame
$admin_session =& $this->recallObject('Session.admin');
/* @var $admin_session Session */
- $user = (int)$admin_session->RecallVar('user_id'); // in case, when no valid admin session found
+ // store Admin Console User's ID to Front-End's session for cross-session permission checks
+ $this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
- $perm_helper =& $this->recallObject('PermissionsHelper');
- /* @var $perm_helper kPermissionsHelper */
-
- if ( $perm_helper->CheckUserPermission($user, 'CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
- // user can edit cms blocks
+ if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) {
+ // user can edit cms blocks (when viewing front-end through admin's frame)
$editing_mode = $this->GetVar('editing_mode');
define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE);
}
@@ -2411,6 +2408,22 @@
return $perm_helper->CheckPermission($name, $type, $cat_id);
}
+ /**
+ * Check current admin permissions based on it's group permissions in specified category
+ *
+ * @param string $name permission name
+ * @param int $cat_id category id, current used if not specified
+ * @param int $type permission type {1 - system, 0 - per category}
+ * @return int
+ */
+ function CheckAdminPermission($name, $type = 1, $cat_id = null)
+ {
+ $perm_helper =& $this->recallObject('PermissionsHelper');
+ /* @var $perm_helper kPermissionsHelper */
+
+ return $perm_helper->CheckAdminPermission($name, $type, $cat_id);
+ }
+
/**
* Set's any field of current visit
*
Index: branches/5.2.x/core/units/content/content_config.php
===================================================================
diff -u -N -r14585 -r14856
--- branches/5.2.x/core/units/content/content_config.php (.../content_config.php) (revision 14585)
+++ branches/5.2.x/core/units/content/content_config.php (.../content_config.php) (revision 14856)
@@ -1,6 +1,6 @@
'PageContentId',
- 'ParentTableKey' => 'CategoryId', // linked field in master table
- 'ForeignKey' => 'PageId', // linked field in subtable
+ 'ParentTableKey' => Array ('c' => 'CategoryId', 'st' => 'CategoryId', 'page-revision' => 'RevisionId'), // linked field in master table
+ 'ForeignKey' => Array ('c' => 'PageId', 'st' => 'PageId', 'page-revision' => 'RevisionId'), // linked field in subtable
'ParentPrefix' => 'c',
'AutoDelete' => true,
'AutoClone' => true,
@@ -48,17 +48,22 @@
'TableName' => TABLE_PREFIX . 'PageContent',
- 'ListSQLs' => Array ('' => 'SELECT * FROM %s'),
+ 'ListSQLs' => Array (
+ '' => ' SELECT %1$s.* %2$s
+ FROM %1$s
+ JOIN ' . TABLE_PREFIX . '%3$sPageRevisions pr ON pr.PageId = %1$s.PageId AND pr.RevisionId = %1$s.RevisionId'
+ ),
'ListSortings' => Array (
'' => Array (
- 'Sorting' => Array ('ContentNum' => 'asc'),
+ 'Sorting' => Array ('ContentNum' => 'asc', 'RevisionNumber' => 'desc'),
)
),
'Fields' => Array (
'PageContentId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'ContentNum' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'PageId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
+ 'RevisionId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'Content' => Array (
'type' => 'string', 'min_len' => 0, 'max_len' => 65536,
'formatter' => 'kMultiLanguage', 'using_fck' => 1,
Index: branches/5.2.x/core/units/categories/categories_tag_processor.php
===================================================================
diff -u -N -r14748 -r14856
--- branches/5.2.x/core/units/categories/categories_tag_processor.php (.../categories_tag_processor.php) (revision 14748)
+++ branches/5.2.x/core/units/categories/categories_tag_processor.php (.../categories_tag_processor.php) (revision 14856)
@@ -1,6 +1,6 @@
_getPage($params);
/* @var $page kDBItem */
- if (!$page->isLoaded()) {
+ if ( !$page->isLoaded() ) {
// page is not created yet => all blocks are empty
return '';
}
- $page_id = $page->GetID();
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
$content =& $this->Application->recallObject('content.-block', null, Array ('skip_autoload' => true));
/* @var $content kDBItem */
- $data = Array ('PageId' => $page_id, 'ContentNum' => $num);
- $content->Load($data);
-
- if (!$content->isLoaded()) {
- // bug: missing content blocks are created even if user have no SMS-management rights
- $content->SetFieldsFromHash($data);
- $content->Create();
+ if ( !$page_helper->loadContentBlock($content, $page, $num) && EDITING_MODE ) {
+ $page_helper->createNewContentBlock($page->GetID(), $num);
+ $page_helper->loadContentBlock($content, $page, $num);
}
$edit_code_before = $edit_code_after = '';
@@ -1364,7 +1361,7 @@
$js_url . '/../incs/cms.css',
);
- $css_compressed = $minify_helper->CompressScriptTag(Array ('files' => implode('|', $to_compress)));
+ $css_compressed = $minify_helper->CompressScriptTag(Array ('files' => implode('|', $to_compress), 'templates_base' => $js_url . '/../'));
$ret = '' . "\n";
@@ -1381,6 +1378,7 @@
$js_url . '/is.js',
$js_url . '/application.js',
$js_url . '/script.js',
+ $js_url . '/toolbar.js',
$js_url . '/jquery/thickbox/thickbox.js',
$js_url . '/template_manager.js',
);
@@ -1400,15 +1398,40 @@
$url_params = Array ('theme-file_event' => 'OnSaveLayout', 'source' => $template, 'pass' => 'all,theme-file', '__NO_REWRITE__' => 1, 'no_amp' => 1);
$save_layout_url = $this->Application->HREF('index', '', $url_params);
- $this_url = $this->Application->HREF('', '', Array ('editing_mode' => '#EDITING_MODE#', '__NO_REWRITE__' => 1, 'no_amp' => 1));
- $ret .= "var aTemplateManager = new TemplateManager('" . $edit_template_url . "', '" . $this_url . "', '" . $save_layout_url . "', " . (int)EDITING_MODE . ");\n";
+ $page =& $this->_getPage($params);
+
+ $url_params = Array(
+ 'pass' => 'm,c',
+ 'c_id' => $page->GetID(),
+ 'c_event' => 'OnGetPageInfo',
+ '__URLENCODE__' => 1,
+ '__NO_REWRITE__'=> 1,
+ 'index_file' => 'index.php',
+ );
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ $class_params = Array (
+ 'pageId' => $page->GetID(),
+ 'pageInfo' => $page_helper->getPageInfo( $page->GetID() ),
+ 'editUrl' => $edit_template_url,
+ 'browseUrl' => $this->Application->HREF('', '', Array ('editing_mode' => '#EDITING_MODE#', '__NO_REWRITE__' => 1, 'no_amp' => 1)),
+ 'saveLayoutUrl' => $save_layout_url,
+ 'editingMode' => (int)EDITING_MODE,
+ );
+
+ $ret .= "var aTemplateManager = new TemplateManager(" . json_encode($class_params) . ");\n";
$ret .= "var main_title = '" . addslashes( $this->Application->ConfigValue('Site_Name') ) . "';" . "\n";
$use_popups = (int)$this->Application->ConfigValue('UsePopups');
$ret .= "var \$use_popups = " . ($use_popups > 0 ? 'true' : 'false') . ";\n";
$ret .= "var \$modal_windows = " . ($use_popups == 2 ? 'true' : 'false') . ";\n";
if ( EDITING_MODE != EDITING_MODE_BROWSE ) {
+ $ret .= 'var $visible_toolbar_buttons = true' . ";\n";
+ $ret .= 'var $use_toolbarlabels = ' . ($this->Application->ConfigValue('UseToolbarLabels') ? 'true' : 'false') . ";\n";;
+
$ret .= "var base_url = '" . $this->Application->BaseURL() . "';" . "\n";
$ret .= 'TB.closeHtml = \'
\';' . "\n";
@@ -1451,6 +1474,11 @@
*/
function EditPage($params)
{
+ if ( $this->Application->GetVar('preview') ) {
+ // prevents draft preview function to replace last template in session and break page/content block editing process
+ $this->Application->SetVar('skip_last_template', 1);
+ }
+
if (!EDITING_MODE) {
return '';
}
@@ -1533,6 +1561,77 @@
if ($display_mode == 'start') {
// button with border around the page
+ if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
+ $tabs = "\n" . str_repeat("\t", 9);
+ $base_url = $this->Application->BaseURL();
+ $toolbar_hidden = $this->Application->GetVar('toolbar_hidden');
+
+ $edit_code .= '
+
+
+ ' . "\n";
+ }
+
$edit_code .= '' . $edit_btn . '
';
}
@@ -1548,6 +1647,29 @@
}
if ($display_mode != 'end') {
+ if ( EDITING_MODE == EDITING_MODE_CONTENT ) {
+ $url_params = Array(
+ 'pass' => 'm',
+ 'm_opener' => 'd',
+ 'm_cat_id' => $page->GetID(),
+ '__URLENCODE__' => 1,
+ '__NO_REWRITE__'=> 1,
+ 'front' => 1,
+ 'index_file' => 'index.php',
+ );
+
+ $revision = $this->Application->GetVar('revision');
+
+ if ( $revision ) {
+ $url_params['revision'] = $revision;
+ }
+
+ $page_admin_url = $this->Application->HREF('', ADMIN_DIRECTORY, $url_params);
+ $edit_code .= '
';
+ }
+
$edit_code .= '
';
// when "EditingScripts" tag is not used, make sure, that scripts are also included
@@ -1557,6 +1679,13 @@
return $edit_code;
}
+ function toolbarButton($name, $title, $tabs)
+ {
+ $phrase = $this->Application->Phrase($title, false, true);
+
+ return $tabs . 'a_toolbar.AddButton( new ToolBarButton("' . $name . '", "' . htmlspecialchars($phrase) . '") );';
+ }
+
function _getThemeFileId()
{
$template = $this->Application->GetVar('t');
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/history_item_background_hover.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/admin_templates/js/script.js
===================================================================
diff -u -N -r14705 -r14856
--- branches/5.2.x/core/admin_templates/js/script.js (.../script.js) (revision 14705)
+++ branches/5.2.x/core/admin_templates/js/script.js (.../script.js) (revision 14856)
@@ -130,6 +130,29 @@
set_hidden_field('remove_specials[' + prefix_special + ']', null);
}
+function submit_event_ajax(prefix_special, event, t, $callback) {
+ if ( !Application.processHooks(prefix_special + ':' + event) ) {
+ return false;
+ }
+
+ if (event) {
+ set_hidden_field('events[' + prefix_special + ']', event);
+ }
+
+ if (t) {
+ set_hidden_field('t', t);
+ }
+
+ var $form = $('#kernel_form'),
+ $from_params = $form.serialize();
+
+ $.post($form.attr('action'), $from_params, $callback);
+
+ // reset remove special mark (otherwise all future events will have special removed too)
+ set_hidden_field('events[' + prefix_special + ']', '');
+ set_hidden_field('remove_specials[' + prefix_special + ']', null);
+}
+
function submit_action($url, $action)
{
$form = document.getElementById($form_name);
Index: branches/5.2.x/core/admin_templates/img/top_frame/revision_control/history_item_background.gif
===================================================================
diff -u -N
Binary files differ
Index: branches/5.2.x/core/kernel/db/db_event_handler.php
===================================================================
diff -u -N -r14855 -r14856
--- branches/5.2.x/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 14855)
+++ branches/5.2.x/core/kernel/db/db_event_handler.php (.../db_event_handler.php) (revision 14856)
@@ -1,6 +1,6 @@
reset();
- $object->linkToParent( $this->getMainSpecial($event) );
+ if ( $event->getEventParam('skip_parent_filter') === false ) {
+ $object->linkToParent( $this->getMainSpecial($event) );
+ }
$this->AddFilters($event);
$this->SetCustomQuery($event); // new!, use this for dynamic queries based on specials for ex.
Index: branches/5.2.x/core/admin_templates/js/template_manager.js
===================================================================
diff -u -N -r14244 -r14856
--- branches/5.2.x/core/admin_templates/js/template_manager.js (.../template_manager.js) (revision 14244)
+++ branches/5.2.x/core/admin_templates/js/template_manager.js (.../template_manager.js) (revision 14856)
@@ -1,13 +1,18 @@
-function TemplateManager ($edit_url, $browse_url, $save_layout_url, $edting_mode) {
- this._editUrl = $edit_url;
- this.browseUrl = $browse_url;
- this._saveLayoutUrl = $save_layout_url;
- this.editingMode = $edting_mode; // from {1 - browse, 2 - content, 3 - design}
+function TemplateManager ( $settings ) {
+ this.pageId = 0;
+ this.editUrl = '';
+ this.browseUrl = '';
+ this.saveLayoutUrl = '';
+ this.editingMode = 0; // from {1 - browse, 2 - content, 3 - design}
+ this.pageInfo = {editors: [], revisions: {}}; // information about page in "Content Mode"
+
this._blocks = {};
this._blockOrder = Array ();
this.inDrag = false; // don't process mouse over/out events while in drag mode
+ $.extend(this, $settings);
+
var $template_manager = this;
$(document).ready(
@@ -51,6 +56,20 @@
// make all spans with phrases clickable
$template_manager.setupEditTranslationButtons(document);
+
+ // hide "Revision History" div on every body click (bubbled), but not a "toolbar button", that opens it
+ $('body').click(
+ function ($e) {
+ var $target = $($e.target),
+ $id = $target.attr('id');
+
+ if ( $id && ($id == 'tool_history' || $id == 'div_history') ) {
+ return ;
+ }
+
+ $('#cms-revision-dropdown:visible').hide();
+ }
+ );
}
if ($template_manager.editingMode == 3) {
@@ -96,10 +115,196 @@
$(this).css('opacity', 0.5);
}
);
+
+ // related to content revision control toolbar
+ $('#cms-toggle-revision-toolbar').click(
+ function ($e) {
+ var $me = $(this);
+
+ if ( $me.hasClass('opened') ) {
+ var $height = $('#cms-revision-toolbar').height();
+
+ $('#cms-revision-toolbar-layer').animate({top: (-1) * $height}, 'fast');
+ $('#cms-editing-notice, #cms-revision-dropdown').hide();
+ setCookie('toolbar_hidden', 1);
+ }
+ else {
+ $('#cms-revision-toolbar-layer').animate({top: 0}, 'fast');
+ setCookie('toolbar_hidden', 0);
+ }
+
+ $me.toggleClass('opened');
+
+ return false;
+ }
+ );
+
+ $('#cms-close-toolbar').click(
+ function () {
+ var $height = $('#cms-revision-toolbar').height();
+
+ $('#cms-toggle-revision-toolbar').removeClass('opened');
+ $('#cms-revision-toolbar-layer').css('top', (-1) * $height);
+
+ $('#cms-editing-notice, #cms-revision-dropdown').hide();
+ setCookie('toolbar_hidden', 1);
+
+ return false;
+ }
+ );
+
+ $('#cms-close-editing-notice').click(
+ function () {
+ $('#cms-editing-notice').hide();
+
+ return false;
+ }
+ );
+
+ $('.toolbar-button', '#cms-revision-toolbar').click(
+ function ($e) {
+ var $button_name = $(this).attr('id').replace(/^(tool|div)_/, '');
+
+ $template_manager.revisionToolbarClick($button_name);
+ }
+ );
+
+ setInterval(
+ function () {
+ $.getJSON(
+ $('#kf_revisions_' + $template_manager.pageId).attr('action') + '&events[page-revision]=OnGetInfo',
+ function ($data) {
+ $template_manager.pageInfo = $data;
+ $template_manager.processPageInfo();
+ }
+ );
+ }, 20 * 1000 // 20 seconds
+ );
+
+ if ( !$.isEmptyObject($template_manager.pageInfo) ) {
+ $template_manager.processPageInfo();
+ }
}
);
}
+TemplateManager.prototype.processPageInfo = function () {
+ var $class_mapping = {
+ 1: 'cms-revision-published',
+ 2: 'cms-revision-pending',
+ 0: 'cms-revision-declined'
+ };
+
+ var $title = $('.revision-title', '#cms-current-revision-info');
+
+ $title.html( this.pageInfo.current_revision.title );
+ $('.draft-saved', '#cms-current-revision-info').html( this.pageInfo.current_revision.saved );
+
+ for (var $status in $class_mapping) {
+ $title.toggleClass( $class_mapping[$status], $status === this.pageInfo.current_revision.status );
+ }
+
+ if ( $('#cms-toggle-revision-toolbar').hasClass('opened') ) {
+ var $notice = $('#cms-editing-notice');
+
+ if ( this.pageInfo.editors.length ) {
+ if ( $('span:first', $notice).attr('prev_editors') != this.pageInfo.editors.join(',') ) {
+ // show notice, only when editors change occurs
+ $('span:first', $notice).html(this.pageInfo.editors_warning).attr('prev_editors', this.pageInfo.editors.join(','));
+
+ if ( $notice.is(':hidden') ) {
+ $notice.fadeIn();
+ }
+ }
+ }
+ else if ( $notice.is(':visible') ) {
+ $notice.fadeOut();
+ }
+ }
+
+ var $revision_container = $('.top', '#cms-revision-dropdown'),
+ $revision_mask = '
\
+
{TITLE} ({STATUS_LABEL})\
+
{DATETIME}
\
+
{AUTHOR}
\
+
\
+
';
+
+ $revision_container.empty();
+
+ if ( $.isArray(this.pageInfo.revisions) ) {
+ // no revisions yet
+ }
+ else {
+ for (var $revision in this.pageInfo.revisions) {
+ var $html = $revision_mask,
+ $revision_info = this.pageInfo.revisions[$revision];
+
+ for (var $field in $revision_info) {
+ $html = $html.replace( new RegExp('{' + $field.toUpperCase() + '}', 'g'), $revision_info[$field] );
+ }
+
+ $html = $html.replace(/{CLASS}/g, $class_mapping[$revision_info.status] );
+
+ if ( $revision_info['draft'] ) {
+ $html = $html.replace(/{LINK}/g, this.browseUrl.replace('#EDITING_MODE#', 2) );
+ }
+ else {
+ $html = $html.replace(/{LINK}/g, this.browseUrl.replace('#EDITING_MODE#', 2) + '&revision=' + $revision.substr(1) );
+ }
+
+ $revision_container.append($html);
+ }
+
+ $('.item', '#cms-revision-dropdown .top').each(
+ function () {
+ var $row = $(this);
+
+ $('a:first', $row).click(
+ function ($e) {
+ $e.stopPropagation();
+ }
+ );
+
+ $row.click(
+ function ($e) {
+ window.location.href = $('a:first', this).attr('href');
+ }
+ );
+ }
+ );
+ }
+}
+
+TemplateManager.prototype.revisionToolbarClick = function ($button_name) {
+// console.log('button ', $button_name, ' clicked');
+
+ var $button_event_map = {
+ 'select': 'OnSave',
+ 'delete': 'OnDiscard',
+ 'approve': 'OnPublish',
+ 'decline': 'OnDecline'
+ };
+
+ if ( $button_event_map[$button_name] !== undefined ) {
+ $form_name = 'kf_revisions_' + this.pageId;
+ submit_event('page-revision', $button_event_map[$button_name]);
+
+ return ;
+ }
+
+ switch ( $button_name ) {
+ case 'preview':
+ var $url = this.browseUrl.replace('#EDITING_MODE#', 0).replace(/&(admin|editing_mode)=[\d]/g, '');
+ window.open( $url + '&preview=1' );
+ break;
+
+ case 'history':
+ $('#cms-revision-dropdown').toggle();
+ break;
+ }
+}
+
TemplateManager.prototype.setupEditTranslationButtons = function ($container) {
$("span[name='cms-translate-phrase']", $container).each(
function() {
@@ -198,7 +403,7 @@
var $me = this;
var $settings = {
- url: this._saveLayoutUrl + '&' + $sort_order + '&width=200&height=70&modal=true',
+ url: this.saveLayoutUrl + '&' + $sort_order + '&width=200&height=70&modal=true',
caption: 'Layout Saving Result',
onDataReceived: function ($data) {
var $message = '';
@@ -229,7 +434,7 @@
TemplateManager.prototype.onBtnClick = function ($e, $element) {
var $id = $element.id.replace(/_btn$/, '');
var $block_info = this._blocks[$id];
- var $url = this._editUrl.replace('#BLOCK#', $block_info.block_name + ':' + $block_info.function_name).replace('#EVENT#', 'OnLoadBlock');
+ var $url = this.editUrl.replace('#BLOCK#', $block_info.block_name + ':' + $block_info.function_name).replace('#EVENT#', 'OnLoadBlock');
direct_edit('theme-file', $url);
Index: branches/5.2.x/core/admin_templates/img/toolbar/tool_history_f2.gif
===================================================================
diff -u -N
Binary files differ