Index: admin_templates/categories/edit_content.tpl
===================================================================
--- admin_templates/categories/edit_content.tpl (revision 14476)
+++ admin_templates/categories/edit_content.tpl (working copy)
@@ -16,7 +16,7 @@
'select',
'',
function() {
- submit_event('content','');
+ submit_event('content', 'OnSaveContentBlock');
}
)
);
@@ -24,23 +24,13 @@
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();
@@ -71,4 +61,35 @@
+
+
\ No newline at end of file
Index: admin_templates/img/toolbar/tool_history.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_history.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/toolbar/tool_history_f2.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_history_f2.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/toolbar/tool_history_f3.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_history_f3.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/toolbar/tool_preview.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_preview.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/toolbar/tool_preview_f2.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_preview_f2.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/toolbar/tool_preview_f3.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\toolbar\tool_preview_f3.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control
___________________________________________________________________
Added: tsvn:autoprops
+ *.php = svn:eol-style=LF;svn:keywords=Id
*.tpl = svn:eol-style=LF
*.sql = svn:eol-style=LF
*.lang = svn:eol-style=LF
*.sh = svn:eol-style=LF;svn:executable
*.txt = svn:eol-style=LF
*.html = svn:eol-style=LF
*.htm = svn:eol-style=LF
*.css = svn:eol-style=LF
*.js = svn:eol-style=LF
*.xml = svn:eol-style=LF
.htaccess = svn:eol-style=LF
.smsignore = svn:eol-style=LF
COPYRIGHT = svn:eol-style=LF
CREDITS = svn:eol-style=LF
INSTALL = svn:eol-style=LF
LICENSE = svn:eol-style=LF
LICENSES = svn:eol-style=LF
README = svn:eol-style=LF
Added: bugtraq:url
+ http://tracker.in-portal.org/view.php?id=%BUGID%
Added: bugtraq:logregex
+ (?:[Bb]ugs?|[Ii]ssues?|[Rr]eports?|[Ff]ixe?s?|[Rr]esolves?)+\s+(?:#?(?:\d+)[,\.\s]*)+
(\d+)
Index: admin_templates/img/top_frame/revision_control/button_vp_left.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\button_vp_left.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/button_vp_right.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\button_vp_right.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/button_vp_right2.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\button_vp_right2.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/close_black.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\close_black.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/close_white.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\close_white.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/history_bottom.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\history_bottom.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/history_item_background.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\history_item_background.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/history_item_background_hover.gif
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\history_item_background_hover.gif
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/img/top_frame/revision_control/message_background_red.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: admin_templates\img\top_frame\revision_control\message_background_red.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: admin_templates/incs/cms.css
===================================================================
--- admin_templates/incs/cms.css (revision 14476)
+++ admin_templates/incs/cms.css (working copy)
@@ -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: admin_templates/js/script.js
===================================================================
--- admin_templates/js/script.js (revision 14476)
+++ admin_templates/js/script.js (working copy)
@@ -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: admin_templates/js/template_manager.js
===================================================================
--- admin_templates/js/template_manager.js (revision 14476)
+++ admin_templates/js/template_manager.js (working copy)
@@ -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 = '
' . $edit_btn . '
';
-
}
else {
// button without border around the page
@@ -1530,6 +1629,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
@@ -1539,6 +1661,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: units/content/content_config.php
===================================================================
--- units/content/content_config.php (revision 14476)
+++ units/content/content_config.php (working copy)
@@ -30,14 +30,15 @@
),
'IDField' => '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,
- 'TitleField' => 'ContentNum', // field, used in bluebar when editing existing item
-
+ 'TitleField' => 'ContentNum', // field, used in bluebar when editing existing item
+
'TitlePresets' => Array (
'default' => Array (
'new_status_labels' => Array ('content' => '!la_title_Adding_Content!'),
@@ -48,10 +49,14 @@
'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'),
)
),
@@ -59,6 +64,7 @@
'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', 'formatter' => 'kMultiLanguage', 'using_fck' => 1, 'default' => ''),
),
Index: units/content/content_eh.php
===================================================================
--- units/content/content_eh.php (revision 14476)
+++ units/content/content_eh.php (working copy)
@@ -34,4 +34,171 @@
return $perm_helper->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: units/helpers/helpers_config.php
===================================================================
--- units/helpers/helpers_config.php (revision 14476)
+++ units/helpers/helpers_config.php (working copy)
@@ -68,5 +68,6 @@
Array ('pseudo' => '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'),
),
);
\ No newline at end of file
Index: units/helpers/minifiers/minify_helper.php
===================================================================
--- units/helpers/minifiers/minify_helper.php (revision 14476)
+++ units/helpers/minifiers/minify_helper.php (working copy)
@@ -124,7 +124,13 @@
}
// replace templates base
- $templates_base = $this->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: units/helpers/page_helper.php
===================================================================
--- units/helpers/page_helper.php (revision 0)
+++ units/helpers/page_helper.php (revision 0)
@@ -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.Login 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;
+ }
+}
\ No newline at end of file
Index: units/helpers/permissions_helper.php
===================================================================
--- units/helpers/permissions_helper.php (revision 14476)
+++ units/helpers/permissions_helper.php (working copy)
@@ -389,6 +389,7 @@
$perm_prefix = getArrayValue($params, 'perm_prefix');
$perm_event = getArrayValue($params, 'perm_event');
$permission_groups = getArrayValue($params, 'permissions');
+ $check_admin = isset($params['admin']) && $params['admin'];
if ($permission_groups && !$perm_event) {
// check permissions by permission names in current category
@@ -404,12 +405,22 @@
$is_system = isset($params['system']) && $params['system'] ? 1 : 0;
foreach ($permission_groups as $permission_group) {
+ $has_permission = true;
$permissions = explode(',', $permission_group);
- $has_permission = true;
- 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;
+
+ 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) {
@@ -532,8 +543,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 $name == 'SYSTEM_ACCESS.READONLY' ? 0 : 1;
@@ -554,18 +585,27 @@
// 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 = $this->Application->RecallVar('UserGroups');
+ }
+ else {
+ // checking not current user
+ $groups = $this->Application->RecallVar('UserGroups:' . $user_id);
- if ($user_id == $this->Application->RecallVar('user_id')) {
- $groups = explode(',', $this->Application->RecallVar('UserGroups'));
+ if ( $groups === false ) {
+ $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);
+ }
}
- 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') );
- }
+ $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: units/page_revisions/page_revision_eh.php
===================================================================
--- units/page_revisions/page_revision_eh.php (revision 0)
+++ units/page_revisions/page_revision_eh.php (revision 0)
@@ -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;
+ }
+}
\ No newline at end of file
Index: units/page_revisions/page_revision_tp.php
===================================================================
--- units/page_revisions/page_revision_tp.php (revision 0)
+++ units/page_revisions/page_revision_tp.php (revision 0)
@@ -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') );
+ }
+}
\ No newline at end of file
Index: units/page_revisions/page_revisions_config.php
===================================================================
--- units/page_revisions/page_revisions_config.php (revision 0)
+++ units/page_revisions/page_revisions_config.php (revision 0)
@@ -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.Login',
+ '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' => 'Login', '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: units/structure/structure_config.php
===================================================================
--- units/structure/structure_config.php (revision 14476)
+++ units/structure/structure_config.php (working copy)
@@ -104,7 +104,7 @@
'-virtual' => '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),
),
'VirtualFields' => Array(