Attached Files |
Versions_Control_Editorial_Interfaces_3_c.jpg [^] (357,440 bytes) 2011-09-07 18:40
Versions_Control_Editorial_Interfaces_3_a1.jpg [^] (374,510 bytes) 2011-09-07 18:40
Versions_Control_Editorial_Interfaces_3_b1.jpg [^] (384,337 bytes) 2011-09-07 18:40
intechnic4_html.zip [^] (895,389 bytes) 2011-09-07 18:40
images_v2.rar [^] (24,295 bytes) 2011-09-07 18:43
cms_revision_control_feature_v8.patch [^] (98,301 bytes) 2011-09-07 18:44
[Show Content]
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',
'<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>',
function() {
- submit_event('content','<inp2:content_SaveEvent/>');
+ submit_event('content', 'OnSaveContentBlock');
}
)
);
@@ -24,23 +24,13 @@
a_toolbar.AddButton(
new ToolBarButton(
'cancel',
- '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>',
+ '<inp2:m_phrase label="la_ToolTip_Close" escape="1"/>',
function() {
- cancel_edit('content','OnCancelEdit','<inp2:content_SaveEvent/>','<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>');
+ window_close();
}
)
);
- a_toolbar.AddButton(
- new ToolBarButton(
- 'reset_edit',
- '<inp2:m_phrase label="la_ToolTip_Reset" escape="1"/>',
- function() {
- reset_form('content', 'OnReset', '<inp2:m_Phrase label="la_FormResetConfirmation" escape="1"/>');
- }
- )
- );
-
a_toolbar.Render();
</script>
@@ -71,4 +61,35 @@
<input type="hidden" name="c_id" value="<inp2:m_Get name='c_id'/>"/>
+<script type="text/javascript">
+ var $autosave_title = '<inp2:m_Phrase name="la_title_EditingContentAutosaved" no_editing="1" js_escape="1"/>';
+
+ setInterval(
+ function () {
+ var $editor_instance = FCKeditorAPI.GetInstance('<inp2:content_InputName field="Content"/>');
+ $('#' + jq('<inp2:content_InputName field="Content"/>')).val( $editor_instance.GetHTML() );
+
+ submit_event_ajax(
+ 'content', 'OnAutoSave', undefined,
+ function ($autosave_string) {
+ $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
+ }
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL"/> * 1000
+ );
+
+ setInterval(
+ function () {
+ submit_event_ajax(
+ 'content', 'OnGetAutoSaveTime', undefined,
+ function ($autosave_string) {
+ if ( $autosave_string ) {
+ $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
+ }
+ }
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL"/> * 1000
+ );
+</script>
+
<inp2:m_include t="incs/footer"/>
\ 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 = ' <div class="item">\
+ <span class="{CLASS}"><a href="{LINK}">{TITLE}</a> ({STATUS_LABEL})</span>\
+ <div class="cms-left">{DATETIME}</div>\
+ <div class="cms-right">{AUTHOR}</div>\
+ <div class="cms-clear"></div>\
+ </div>';
+
+ $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: admin_templates/js/toolbar.js
===================================================================
--- admin_templates/js/toolbar.js (revision 14476)
+++ admin_templates/js/toolbar.js (working copy)
@@ -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: install/english.lang
===================================================================
--- install/english.lang (revision 14487)
+++ install/english.lang (working copy)
@@ -35,6 +35,7 @@
<PHRASE Label="la_btn_Locate" Module="Core" Type="1">TG9jYXRl</PHRASE>
<PHRASE Label="la_btn_MoveDown" Module="Core" Type="1">TW92ZSBEb3du</PHRASE>
<PHRASE Label="la_btn_MoveUp" Module="Core" Type="1">TW92ZSBVcA==</PHRASE>
+ <PHRASE Label="la_btn_PublishingTools" Module="Core" Type="1">UHVibGlzaGluZyBUb29scw==</PHRASE>
<PHRASE Label="la_btn_Rebuild" Module="Core" Type="1">UmVidWlsZA==</PHRASE>
<PHRASE Label="la_btn_Recompile" Module="Core" Type="1">UmVjb21waWxl</PHRASE>
<PHRASE Label="la_btn_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE>
@@ -51,6 +52,7 @@
<PHRASE Label="la_btn_Unselect" Module="Core" Type="1">VW5zZWxlY3Q=</PHRASE>
<PHRASE Label="la_btn_Up" Module="Core" Type="1">VXA=</PHRASE>
<PHRASE Label="la_btn_UseDraft" Module="Core" Type="1">VXNl</PHRASE>
+ <PHRASE Label="la_By" Module="Core" Type="1">Ynk=</PHRASE>
<PHRASE Label="la_Cancel" Module="Core" Type="1">Q2FuY2Vs</PHRASE>
<PHRASE Label="la_category" Module="Core" Type="1">U2VjdGlvbg==</PHRASE>
<PHRASE Label="la_category_daysnew_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGRheXMgZm9yIGEgY2F0LiB0byBiZSBORVc=</PHRASE>
@@ -318,7 +320,9 @@
<PHRASE Label="la_DownloadCSV" Module="Core" Type="1">RG93bmxvYWQgQ1NW</PHRASE>
<PHRASE Label="la_DownloadExportFile" Module="Core" Type="1">RG93bmxvYWQgRXhwb3J0IEZpbGU=</PHRASE>
<PHRASE Label="la_DownloadLanguageExport" Module="Core" Type="1">RG93bmxvYWQgTGFuZ3VhZ2UgRXhwb3J0</PHRASE>
+ <PHRASE Label="la_Draft" Module="Core" Type="1">RHJhZnQ=</PHRASE>
<PHRASE Label="la_DraftAvailableFrom" Module="Core" Type="1">RHJhZnQgQXZhaWxhYmxl</PHRASE>
+ <PHRASE Label="la_DraftSavedAt" Module="Core" Type="1">ZHJhZnQgc2F2ZWQgYXQgJXM=</PHRASE>
<PHRASE Label="la_EditingContent" Module="Core" Type="1">Q29udGVudCBFZGl0b3I=</PHRASE>
<PHRASE Label="la_EditingInProgress" Module="Core" Type="1">WW91IGhhdmUgbm90IHNhdmVkIGNoYW5nZXMgdG8gdGhlIGl0ZW0geW91IGFyZSBlZGl0aW5nITxiciAvPkNsaWNrIE9LIHRvIGxvb3NlIGNoYW5nZXMgYW5kIGdvIHRvIHRoZSBzZWxlY3RlZCBzZWN0aW9uPGJyIC8+b3IgQ2FuY2VsIHRvIHN0YXkgaW4gdGhlIGN1cnJlbnQgc2VjdGlvbi4=</PHRASE>
<PHRASE Label="la_editor_default_style" Module="Core" Type="1">RGVmYXVsdCB0ZXh0</PHRASE>
@@ -805,6 +809,7 @@
<PHRASE Label="la_opt_CustomRecipients" Module="Core" Type="1">Q3VzdG9tICJUbyIgUmVjaXBpZW50KC1zKQ==</PHRASE>
<PHRASE Label="la_opt_CustomSender" Module="Core" Type="1">Q3VzdG9tIFNlbmRlcg==</PHRASE>
<PHRASE Label="la_opt_day" Module="Core" Type="1">ZGF5KHMp</PHRASE>
+ <PHRASE Label="la_opt_Declined" Module="Core" Type="1">RGVjbGluZWQ=</PHRASE>
<PHRASE Label="la_opt_DefaultAddress" Module="Core" Type="1">RGVmYXVsdCBXZWJzaXRlIGFkZHJlc3M=</PHRASE>
<PHRASE Label="la_opt_Deny" Module="Core" Type="1">RGVueQ==</PHRASE>
<PHRASE Label="la_opt_Description" Module="Core" Type="1">RGVzY3JpcHRpb24=</PHRASE>
@@ -842,6 +847,7 @@
<PHRASE Label="la_opt_Phone" Module="Core" Type="1">UGhvbmU=</PHRASE>
<PHRASE Label="la_opt_PopupWindow" Module="Core" Type="1">UG9wdXAgV2luZG93</PHRASE>
<PHRASE Label="la_opt_Processed" Module="Core" Type="1">UHJvY2Vzc2Vk</PHRASE>
+ <PHRASE Label="la_opt_Published" Module="Core" Type="1">UHVibGlzaGVk</PHRASE>
<PHRASE Label="la_opt_Rating" Module="Core" Type="1">UmF0aW5n</PHRASE>
<PHRASE Label="la_opt_RecipientEmail" Module="Core" Type="1">UmVjaXBpZW50IEUtbWFpbA==</PHRASE>
<PHRASE Label="la_opt_RecipientName" Module="Core" Type="1">UmVjaXBpZW50IE5hbWU=</PHRASE>
@@ -885,6 +891,9 @@
<PHRASE Label="la_p9" Module="Core" Type="1">KEdNVCArMDk6MDAp</PHRASE>
<PHRASE Label="la_Paddings" Module="Core" Type="1">UGFkZGluZ3M=</PHRASE>
<PHRASE Label="la_Page" Module="Core" Type="1">UGFnZQ==</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing1" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBpcyBjdXJyZW50bHkgZWRpdGluZyB0aGlzIHNlY3Rpb24h</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing2" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uISA=</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing5" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uIQ==</PHRASE>
<PHRASE Label="la_passwords_do_not_match" Module="Core" Type="1">UGFzc3dvcmRzIGRvIG5vdCBtYXRjaA==</PHRASE>
<PHRASE Label="la_passwords_too_short" Module="Core" Type="1">UGFzc3dvcmQgaXMgdG9vIHNob3J0LCBwbGVhc2UgZW50ZXIgYXQgbGVhc3QgJXMgY2hhcmFjdGVycw==</PHRASE>
<PHRASE Label="la_Pending" Module="Core" Type="1">UGVuZGluZw==</PHRASE>
@@ -1046,8 +1055,10 @@
<PHRASE Label="la_RequiredWarning" Module="Core" Type="1">Tm90IGFsbCByZXF1aXJlZCBmaWVsZHMgYXJlIGZpbGxlZC4gUGxlYXNlIGZpbGwgdGhlbSBmaXJzdC4=</PHRASE>
<PHRASE Label="la_review_perpage_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2U=</PHRASE>
<PHRASE Label="la_review_perpage_short_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2UgKHNob3J0LWxpc3Qp</PHRASE>
+ <PHRASE Label="la_RevisionNumber" Module="Core" Type="1">UmV2aXNpb24gIyVz</PHRASE>
<PHRASE Label="la_rootcategory_name" Module="Core" Type="1">SG9tZQ==</PHRASE>
<PHRASE Label="la_SampleText" Module="Core" Type="1">U2FtcGxlIFRleHQ=</PHRASE>
+ <PHRASE Label="la_SavedAt" Module="Core" Type="1">c2F2ZWQgYXQgJXM=</PHRASE>
<PHRASE Label="la_SaveLogin" Module="Core" Type="1">U2F2ZSBVc2VybmFtZSBvbiBUaGlzIENvbXB1dGVy</PHRASE>
<PHRASE Label="la_Search" Module="Core" Type="1">U2VhcmNo</PHRASE>
<PHRASE Label="la_section_BasicPermissions" Module="Core" Type="1">QmFzaWMgUGVybWlzc2lvbnM=</PHRASE>
@@ -1303,7 +1314,9 @@
<PHRASE Label="la_title_EditingAgent" Module="Core" Type="1">RWRpdGluZyBBZ2VudA==</PHRASE>
<PHRASE Label="la_title_EditingBanRule" Module="Core" Type="1">RWRpdGluZyBCYW4gUnVsZQ==</PHRASE>
<PHRASE Label="la_title_EditingChangeLog" Module="Core" Type="1">RWRpdGluZyBDaGFuZ2VzIExvZw==</PHRASE>
+ <PHRASE Label="la_title_EditingContentAutosaved" Module="Core" Type="1">Q29udGVudCBFZGl0b3IgLSBBdXRvLXNhdmVkIGF0ICVz</PHRASE>
<PHRASE Label="la_title_EditingCountryState" Module="Core" Type="1">RWRpdGluZyBDb3VudHJ5L1N0YXRl</PHRASE>
+ <PHRASE Label="la_title_EditingDraft" Module="Core" Type="1">RWRpdGluZyBEcmFmdCAoJTIkcyk=</PHRASE>
<PHRASE Label="la_title_EditingEmailEvent" Module="Core" Type="1">RWRpdGluZyBFbWFpbCBFdmVudA==</PHRASE>
<PHRASE Label="la_title_EditingFile" Module="Core" Type="1">RWRpdGluZyBGaWxl</PHRASE>
<PHRASE Label="la_title_EditingMembership" Module="Core" Type="1">RWRpdGluZyBNZW1iZXJzaGlw</PHRASE>
@@ -1412,6 +1425,7 @@
<PHRASE Label="la_title_ViewingFormSubmission" Module="Core" Type="1">Vmlld2luZyBmb3JtIHN1Ym1pc3Npb24=</PHRASE>
<PHRASE Label="la_title_ViewingMailingList" Module="Core" Type="1">Vmlld2luZyBNYWlsaW5nIExpc3Q=</PHRASE>
<PHRASE Label="la_title_ViewingReply" Module="Core" Type="1">Vmlld2luZyBSZXBseQ==</PHRASE>
+ <PHRASE Label="la_title_ViewingRevision" Module="Core" Type="1">Vmlld2luZyBSZXZpc2lvbiAjJXMgKCVzKQ==</PHRASE>
<PHRASE Label="la_title_Visits" Module="Core" Type="1">VmlzaXRz</PHRASE>
<PHRASE Label="la_title_Website" Module="Core" Type="1">V2Vic2l0ZQ==</PHRASE>
<PHRASE Label="la_ToolTipShort_Edit_Current_Category" Module="Core" Type="1">Q3Vyci4gU2VjdGlvbg==</PHRASE>
@@ -1435,6 +1449,7 @@
<PHRASE Label="la_ToolTip_Deny" Module="Core" Type="1">RGVueQ==</PHRASE>
<PHRASE Label="la_ToolTip_Details" Module="Core" Type="1">RGV0YWlscw==</PHRASE>
<PHRASE Label="la_ToolTip_Disable" Module="Core" Type="1">RGlzYWJsZQ==</PHRASE>
+ <PHRASE Label="la_ToolTip_Discard" Module="Core" Type="1">RGlzY2FyZA==</PHRASE>
<PHRASE Label="la_ToolTip_Edit" Module="Core" Type="1">RWRpdA==</PHRASE>
<PHRASE Label="la_ToolTip_Edit_Current_Category" Module="Core" Type="1">RWRpdCBDdXJyZW50IFNlY3Rpb24=</PHRASE>
<PHRASE Label="la_ToolTip_Email_FrontOnly" Module="Core" Type="1">RnJvbnQtRW5kIE9ubHk=</PHRASE>
@@ -1442,6 +1457,7 @@
<PHRASE Label="la_ToolTip_Export" Module="Core" Type="1">RXhwb3J0</PHRASE>
<PHRASE Label="la_ToolTip_ExportLanguage" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdl</PHRASE>
<PHRASE Label="la_ToolTip_HideMenu" Module="Core" Type="1">SGlkZSBNZW51</PHRASE>
+ <PHRASE Label="la_ToolTip_History" Module="Core" Type="1">SGlzdG9yeQ==</PHRASE>
<PHRASE Label="la_ToolTip_Home" Module="Core" Type="1">SG9tZQ==</PHRASE>
<PHRASE Label="la_ToolTip_Import" Module="Core" Type="1">SW1wb3J0</PHRASE>
<PHRASE Label="la_ToolTip_ImportLanguage" Module="Core" Type="1">SW1wb3J0IExhbmd1YWdl</PHRASE>
@@ -1473,9 +1489,11 @@
<PHRASE Label="la_ToolTip_Next" Module="Core" Type="1">TmV4dA==</PHRASE>
<PHRASE Label="la_ToolTip_Paste" Module="Core" Type="1">UGFzdGU=</PHRASE>
<PHRASE Label="la_ToolTip_Prev" Module="Core" Type="1">UHJldmlvdXM=</PHRASE>
+ <PHRASE Label="la_ToolTip_Preview" Module="Core" Type="1">UHJldmlldw==</PHRASE>
<PHRASE Label="la_ToolTip_PrimaryGroup" Module="Core" Type="1">U2V0IFByaW1hcnkgR3JvdXA=</PHRASE>
<PHRASE Label="la_ToolTip_Print" Module="Core" Type="1">UHJpbnQ=</PHRASE>
<PHRASE Label="la_ToolTip_ProcessQueue" Module="Core" Type="1">UHJvY2VzcyBRdWV1ZQ==</PHRASE>
+ <PHRASE Label="la_ToolTip_Publish" Module="Core" Type="1">UHVibGlzaA==</PHRASE>
<PHRASE Label="la_ToolTip_RebuildCategoryCache" Module="Core" Type="1">UmVidWlsZCBTZWN0aW9uIENhY2hl</PHRASE>
<PHRASE Label="la_ToolTip_RecalculatePriorities" Module="Core" Type="1">UmVjYWxjdWxhdGUgUHJpb3JpdGllcw==</PHRASE>
<PHRASE Label="la_ToolTip_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE>
Index: install/install_data.sql
===================================================================
--- install/install_data.sql (revision 14487)
+++ install/install_data.sql (working copy)
@@ -489,6 +489,11 @@
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.DELETE', 'la_PermName_Category.Delete_desc', 'In-Portal');
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.ADD.PENDING', 'la_PermName_Category.AddPending_desc', 'In-Portal');
INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.MODIFY', 'la_PermName_Category.Modify_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD', 'la_PermName_Category.Revision.Add_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal');
INSERT INTO PermissionConfig VALUES (DEFAULT, 'ADMIN', 'la_PermName_Admin_desc', 'Admin');
INSERT INTO PermissionConfig VALUES (DEFAULT, 'LOGIN', 'la_PermName_Login_desc', 'Front');
INSERT INTO PermissionConfig VALUES (DEFAULT, 'DEBUG.ITEM', 'la_PermName_Debug.Item_desc', 'Admin');
Index: install/install_schema.sql
===================================================================
--- install/install_schema.sql (revision 14476)
+++ install/install_schema.sql (working copy)
@@ -1,9 +1,9 @@
CREATE TABLE PermissionConfig (
- PermissionConfigId int(11) NOT NULL auto_increment,
- PermissionName varchar(30) NOT NULL default '',
- Description varchar(255) NOT NULL default '',
- ModuleId varchar(20) NOT NULL default '0',
- PRIMARY KEY (PermissionConfigId),
+ PermissionConfigId int(11) NOT NULL AUTO_INCREMENT,
+ PermissionName varchar(255) NOT NULL DEFAULT '',
+ Description varchar(255) NOT NULL DEFAULT '',
+ ModuleId varchar(20) NOT NULL DEFAULT '0',
+ PRIMARY KEY (PermissionConfigId),
KEY PermissionName (PermissionName)
);
@@ -460,6 +460,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',
PRIMARY KEY (CategoryId),
UNIQUE KEY ResourceId (ResourceId),
KEY ParentId (ParentId),
@@ -487,7 +488,8 @@
KEY EnablePageCache (EnablePageCache),
KEY OverridePageCacheKey (OverridePageCacheKey),
KEY PageExpiration (PageExpiration),
- KEY Protected (Protected)
+ KEY Protected (Protected),
+ KEY LiveRevisionNumber (LiveRevisionNumber)
);
CREATE TABLE CategoryCustomData (
@@ -988,15 +990,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: install/remove_schema.sql
===================================================================
--- install/remove_schema.sql (revision 14476)
+++ install/remove_schema.sql (working copy)
@@ -61,6 +61,7 @@
DROP TABLE StopWords;
DROP TABLE MailingLists;
DROP TABLE PageContent;
+DROP TABLE PageRevisions;
DROP TABLE FormFields;
DROP TABLE FormSubmissions;
DROP TABLE SubmissionLog;
Index: install/upgrades.php
===================================================================
--- install/upgrades.php (revision 14476)
+++ install/upgrades.php (working copy)
@@ -1768,4 +1768,38 @@
$this->Conn->Query($sql);
}
}
+
+ /**
+ * Update to 5.1.3; Transforms PageContent table
+ *
+ * @param string $mode when called mode {before, after)
+ */
+ function Upgrade_5_1_3($mode)
+ {
+ if ($mode == 'after') {
+ $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: install/upgrades.sql
===================================================================
--- install/upgrades.sql (revision 14487)
+++ install/upgrades.sql (working copy)
@@ -2039,4 +2039,38 @@
UPDATE Phrase
SET l<%PRIMARY_LANGUAGE%>_Translation = 'User name length (min - max)'
-WHERE PhraseKey = 'LA_TEXT_MIN_USERNAME' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Minimum user name length';
\ No newline at end of file
+WHERE PhraseKey = 'LA_TEXT_MIN_USERNAME' AND l<%PRIMARY_LANGUAGE%>_Translation = 'Minimum user name length';
+
+# ===== v 5.1.3 =====
+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');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.ADD.PENDING', 'la_PermName_Category.Revision.Add.Pending_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.MODERATE', 'la_PermName_Category.Revision.Moderate_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.VIEW', 'la_PermName_Category.Revision.History.View_desc', 'In-Portal');
+INSERT INTO PermissionConfig VALUES (DEFAULT, 'CATEGORY.REVISION.HISTORY.RESTORE', 'la_PermName_Category.Revision.History.Restore_desc', 'In-Portal');
\ No newline at end of file
Index: kernel/application.php
===================================================================
--- kernel/application.php (revision 14509)
+++ kernel/application.php (working copy)
@@ -1042,15 +1042,15 @@
safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it
}
}
- elseif ($this->GetVar('admin')) {
- // viewing front-end through admin's frame
+ elseif ( $this->GetVar('admin') ) {
$admin_session =& $this->Application->recallObject('Session.admin');
- $user = (int)$admin_session->RecallVar('user_id'); // in case, when no valid admin session found
- $perm_helper =& $this->recallObject('PermissionsHelper');
- /* @var $perm_helper kPermissionsHelper */
+ /* @var $admin_session Session */
- if ($perm_helper->CheckUserPermission($user, 'CATEGORY.MODIFY', 0, $this->getBaseCategory())) {
- // user can edit cms blocks
+ // store Admin Console User's ID to Front-End's session for cross-session permission checks
+ $this->Application->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id'));
+
+ 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);
}
@@ -3013,10 +3013,28 @@
function CheckPermission($name, $type = 1, $cat_id = null)
{
$perm_helper =& $this->recallObject('PermissionsHelper');
+ /* @var $perm_helper kPermissionsHelper */
+
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
*
* @param string $field
Index: kernel/constants.php
===================================================================
--- kernel/constants.php (revision 14476)
+++ kernel/constants.php (working copy)
@@ -175,4 +175,7 @@
define('RECIPIENT_TYPE_BCC', 3);
define('PAGE_TYPE_VIRTUAL', 1);
- define('PAGE_TYPE_TEMPLATE', 2);
\ No newline at end of file
+ define('PAGE_TYPE_TEMPLATE', 2);
+
+ define('CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL', 5 * 60); // 5 minutes
+ define('CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL', 20); // 20 seconds
\ No newline at end of file
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php (revision 14476)
+++ kernel/db/db_event_handler.php (working copy)
@@ -675,7 +675,9 @@
$object->Counted = false; // when requery="1" should re-count records too!
$object->ClearOrderFields(); // prevents duplicate order fields, when using requery="1"
- $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: kernel/languages/phrases_cache.php
===================================================================
--- kernel/languages/phrases_cache.php (revision 14476)
+++ kernel/languages/phrases_cache.php (working copy)
@@ -145,6 +145,7 @@
else {
if ($this->Application->isAdmin) {
$this->LanguageId = $this->Application->Session->GetField('Language');
+ $this->AdminLanguageId = $this->LanguageId; // same languages, when used from Admin Console
}
else {
$this->LanguageId = $this->Application->GetVar('m_lang');
Index: units/categories/categories_config.php
===================================================================
--- units/categories/categories_config.php (revision 14476)
+++ units/categories/categories_config.php (working copy)
@@ -270,7 +270,7 @@
'-virtual' => '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),
),
'VirtualFields' => Array (
Index: units/categories/categories_tag_processor.php
===================================================================
--- units/categories/categories_tag_processor.php (revision 14476)
+++ units/categories/categories_tag_processor.php (working copy)
@@ -1076,7 +1076,7 @@
* Returns page object based on requested params
*
* @param Array $params
- * @return PagesItem
+ * @return CategoriesItem
*/
function &_getPage($params)
{
@@ -1133,23 +1133,20 @@
$page =& $this->_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 = '';
@@ -1345,7 +1342,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 = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\n";
@@ -1363,6 +1360,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',
);
@@ -1382,8 +1380,31 @@
$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');
@@ -1391,6 +1412,9 @@
$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 = \'<img src="' . $js_url . '/../img/close_window15.gif" width="15" height="15" style="border-width: 0px;" alt="close"/><br/>\';' . "\n";
@@ -1433,6 +1457,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 '';
}
@@ -1515,8 +1544,78 @@
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 .= '
+ <div>
+ <div id="cms-editing-notice">
+ <div class="top">
+ <a href="#" id="cms-close-editing-notice"></a>
+ <span prev_editors=""></span>
+ </div>
+ <div class="bottom"></div>
+ </div>
+
+ <div id="cms-revision-dropdown">
+ <div class="top"></div>
+ <div class="bottom"></div>
+ </div>
+ </div>
+
+ <div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '>
+ <div id="cms-revision-toolbar">
+ <script type="text/javascript">
+ var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/");
+ ' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );';
+
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) {
+ $edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );';
+ }
+
+ $edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs);
+
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) {
+ $edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs);
+ }
+
+ $edit_code .= $tabs . 'a_toolbar.Render();' . "\n";
+
+ $revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $revision kDBItem */
+
+ if ( !$revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("select");' . $tabs . 'a_toolbar.DisableButton("delete");' . $tabs . 'a_toolbar.DisableButton("preview");';
+ }
+
+ if ( $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("approve");';
+ }
+
+ if ( $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("decline");';
+ }
+
+ $publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true);
+
+ $edit_code .= substr($tabs, 0, -1) . '</script>
+
+ <div id="cms-current-revision-info">
+ <span class="revision-title"></span>
+ <div class="draft-saved"></div>
+ </div>
+
+ <a href="#" id="cms-close-toolbar"></a>
+ <div class="cms-clear"></div>
+ </div>
+
+ <a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a>
+ </div>' . "\n";
+ }
+
$edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">';
-
}
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 .= '<form method="POST" style="display: inline; margin: 0px" name="kf_revisions_'.$page->GetID().'" id="kf_revisions_'.$page->GetID().'" action="' . $page_admin_url . '">
+ <input type="hidden" name="revision" value="' . $this->Application->GetVar('revision', 0) . '"/>
+ </form>';
+ }
+
$edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_'.$page->GetID().'" id="kf_'.$page->GetID().'" action="'.$edit_url.'"></form>';
// 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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageHelper extends kHelper {
+
+ /**
+ * Returns page info
+ *
+ * @param int $page_id
+ * @return Array
+ */
+ function getPageInfo($page_id)
+ {
+ list ($user_id, $history_permission) = $this->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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageRevisionEventHandler extends kDBEventHandler {
+
+ /**
+ * Checks permissions of user
+ *
+ * @param kEvent $event
+ */
+ function CheckPermission(&$event)
+ {
+ if ( $event->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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageRevisionTagProcessor extends kDBTagProcessor {
+
+ function LastAutoSaveAgo($params)
+ {
+ $object =& $this->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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+ $config = Array (
+ 'Prefix' => '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(
Versions_Control_Editorial_Interfaces_3.rar [^] (443,352 bytes) 2011-09-08 09:41
cms_revision_control_feature_520.patch [^] (99,239 bytes) 2011-12-08 11:02
[Show Content]
Index: admin_templates/categories/edit_content.tpl
===================================================================
--- admin_templates/categories/edit_content.tpl (revision 14826)
+++ admin_templates/categories/edit_content.tpl (working copy)
@@ -16,7 +16,7 @@
'select',
'<inp2:m_phrase label="la_ToolTip_Save" escape="1"/>',
function() {
- submit_event('content','<inp2:content_SaveEvent/>');
+ submit_event('content', 'OnSaveContentBlock');
}
)
);
@@ -24,23 +24,13 @@
a_toolbar.AddButton(
new ToolBarButton(
'cancel',
- '<inp2:m_phrase label="la_ToolTip_Cancel" escape="1"/>',
+ '<inp2:m_phrase label="la_ToolTip_Close" escape="1"/>',
function() {
- cancel_edit('content','OnCancelEdit','<inp2:content_SaveEvent/>','<inp2:m_Phrase label="la_FormCancelConfirmation" escape="1"/>');
+ window_close();
}
)
);
- a_toolbar.AddButton(
- new ToolBarButton(
- 'reset_edit',
- '<inp2:m_phrase label="la_ToolTip_Reset" escape="1"/>',
- function() {
- reset_form('content', 'OnReset', '<inp2:m_Phrase label="la_FormResetConfirmation" escape="1"/>');
- }
- )
- );
-
a_toolbar.Render();
</script>
@@ -81,4 +71,35 @@
<input type="hidden" name="c_id" value="<inp2:m_Get name='c_id'/>"/>
+<script type="text/javascript">
+ var $autosave_title = '<inp2:m_Phrase name="la_title_EditingContentAutosaved" no_editing="1" js_escape="1"/>';
+
+ setInterval(
+ function () {
+ var $editor_instance = FCKeditorAPI.GetInstance('<inp2:content_InputName field="Content"/>');
+ $('#' + jq('<inp2:content_InputName field="Content"/>')).val( $editor_instance.GetHTML() );
+
+ submit_event_ajax(
+ 'content', 'OnAutoSave', undefined,
+ function ($autosave_string) {
+ $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
+ }
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL"/> * 1000
+ );
+
+ setInterval(
+ function () {
+ submit_event_ajax(
+ 'content', 'OnGetAutoSaveTime', undefined,
+ function ($autosave_string) {
+ if ( $autosave_string ) {
+ $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
+ }
+ }
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL"/> * 1000
+ );
+</script>
+
<inp2:m_include t="incs/footer"/>
\ 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:number
+ true
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 14826)
+++ 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 14826)
+++ 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 14826)
+++ 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 = ' <div class="item">\
+ <span class="{CLASS}"><a href="{LINK}">{TITLE}</a> ({STATUS_LABEL})</span>\
+ <div class="cms-left">{DATETIME}</div>\
+ <div class="cms-right">{AUTHOR}</div>\
+ <div class="cms-clear"></div>\
+ </div>';
+
+ $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: admin_templates/js/toolbar.js
===================================================================
--- admin_templates/js/toolbar.js (revision 14826)
+++ admin_templates/js/toolbar.js (working copy)
@@ -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: install/english.lang
===================================================================
--- install/english.lang (revision 14853)
+++ install/english.lang (working copy)
@@ -37,6 +37,7 @@
<PHRASE Label="la_btn_Locate" Module="Core" Type="1">TG9jYXRl</PHRASE>
<PHRASE Label="la_btn_MoveDown" Module="Core" Type="1">TW92ZSBEb3du</PHRASE>
<PHRASE Label="la_btn_MoveUp" Module="Core" Type="1">TW92ZSBVcA==</PHRASE>
+ <PHRASE Label="la_btn_PublishingTools" Module="Core" Type="1">UHVibGlzaGluZyBUb29scw==</PHRASE>
<PHRASE Label="la_btn_Rebuild" Module="Core" Type="1">UmVidWlsZA==</PHRASE>
<PHRASE Label="la_btn_Recompile" Module="Core" Type="1">UmVjb21waWxl</PHRASE>
<PHRASE Label="la_btn_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE>
@@ -54,6 +55,7 @@
<PHRASE Label="la_btn_Unselect" Module="Core" Type="1">VW5zZWxlY3Q=</PHRASE>
<PHRASE Label="la_btn_Up" Module="Core" Type="1">VXA=</PHRASE>
<PHRASE Label="la_btn_UseDraft" Module="Core" Type="1">VXNl</PHRASE>
+ <PHRASE Label="la_By" Module="Core" Type="1">Ynk=</PHRASE>
<PHRASE Label="la_Cancel" Module="Core" Type="1">Q2FuY2Vs</PHRASE>
<PHRASE Label="la_category" Module="Core" Type="1">U2VjdGlvbg==</PHRASE>
<PHRASE Label="la_category_daysnew_prompt" Module="Core" Type="1">TnVtYmVyIG9mIGRheXMgZm9yIGEgY2F0LiB0byBiZSBORVc=</PHRASE>
@@ -228,7 +230,9 @@
<PHRASE Label="la_DownloadCSV" Module="Core" Type="1">RG93bmxvYWQgQ1NW</PHRASE>
<PHRASE Label="la_DownloadExportFile" Module="Core" Type="1">RG93bmxvYWQgRXhwb3J0IEZpbGU=</PHRASE>
<PHRASE Label="la_DownloadLanguageExport" Module="Core" Type="1">RG93bmxvYWQgTGFuZ3VhZ2UgRXhwb3J0</PHRASE>
+ <PHRASE Label="la_Draft" Module="Core" Type="1">RHJhZnQ=</PHRASE>
<PHRASE Label="la_DraftAvailableFrom" Module="Core" Type="1">RHJhZnQgQXZhaWxhYmxl</PHRASE>
+ <PHRASE Label="la_DraftSavedAt" Module="Core" Type="1">ZHJhZnQgc2F2ZWQgYXQgJXM=</PHRASE>
<PHRASE Label="la_EditingContent" Module="Core" Type="1">Q29udGVudCBFZGl0b3I=</PHRASE>
<PHRASE Label="la_EditingInProgress" Module="Core" Type="1">WW91IGhhdmUgbm90IHNhdmVkIGNoYW5nZXMgdG8gdGhlIGl0ZW0geW91IGFyZSBlZGl0aW5nITxiciAvPkNsaWNrIE9LIHRvIGxvb3NlIGNoYW5nZXMgYW5kIGdvIHRvIHRoZSBzZWxlY3RlZCBzZWN0aW9uPGJyIC8+b3IgQ2FuY2VsIHRvIHN0YXkgaW4gdGhlIGN1cnJlbnQgc2VjdGlvbi4=</PHRASE>
<PHRASE Label="la_editor_default_style" Module="Core" Type="1">RGVmYXVsdCB0ZXh0</PHRASE>
@@ -762,6 +766,7 @@
<PHRASE Label="la_opt_CustomRecipients" Module="Core" Type="1">Q3VzdG9tICJUbyIgUmVjaXBpZW50KC1zKQ==</PHRASE>
<PHRASE Label="la_opt_CustomSender" Module="Core" Type="1">Q3VzdG9tIFNlbmRlcg==</PHRASE>
<PHRASE Label="la_opt_day" Module="Core" Type="1">ZGF5KHMp</PHRASE>
+ <PHRASE Label="la_opt_Declined" Module="Core" Type="1">RGVjbGluZWQ=</PHRASE>
<PHRASE Label="la_opt_DefaultAddress" Module="Core" Type="1">RGVmYXVsdCBXZWJzaXRlIGFkZHJlc3M=</PHRASE>
<PHRASE Label="la_opt_Deny" Module="Core" Type="1">RGVueQ==</PHRASE>
<PHRASE Label="la_opt_Description" Module="Core" Type="1">RGVzY3JpcHRpb24=</PHRASE>
@@ -801,6 +806,7 @@
<PHRASE Label="la_opt_Phone" Module="Core" Type="1">UGhvbmU=</PHRASE>
<PHRASE Label="la_opt_PopupWindow" Module="Core" Type="1">UG9wdXAgV2luZG93</PHRASE>
<PHRASE Label="la_opt_Processed" Module="Core" Type="1">UHJvY2Vzc2Vk</PHRASE>
+ <PHRASE Label="la_opt_Published" Module="Core" Type="1">UHVibGlzaGVk</PHRASE>
<PHRASE Label="la_opt_QueryString" Module="Core" Type="1">UXVlcnkgU3RyaW5nIChTSUQp</PHRASE>
<PHRASE Label="la_opt_Rating" Module="Core" Type="1">UmF0aW5n</PHRASE>
<PHRASE Label="la_opt_RecipientEmail" Module="Core" Type="1">UmVjaXBpZW50IEUtbWFpbA==</PHRASE>
@@ -847,6 +853,9 @@
<PHRASE Label="la_p9" Module="Core" Type="1">KEdNVCArMDk6MDAp</PHRASE>
<PHRASE Label="la_Paddings" Module="Core" Type="1">UGFkZGluZ3M=</PHRASE>
<PHRASE Label="la_Page" Module="Core" Type="1">UGFnZQ==</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing1" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBpcyBjdXJyZW50bHkgZWRpdGluZyB0aGlzIHNlY3Rpb24h</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing2" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uISA=</PHRASE>
+ <PHRASE Label="la_PageCurrentlyEditing5" Module="Core" Type="1">QXR0ZW50aW9uOiAlcyBhcmUgY3VycmVudGx5IGVkaXRpbmcgdGhpcyBzZWN0aW9uIQ==</PHRASE>
<PHRASE Label="la_passwords_do_not_match" Module="Core" Type="1">UGFzc3dvcmRzIGRvIG5vdCBtYXRjaA==</PHRASE>
<PHRASE Label="la_passwords_too_short" Module="Core" Type="1">UGFzc3dvcmQgaXMgdG9vIHNob3J0LCBwbGVhc2UgZW50ZXIgYXQgbGVhc3QgJXMgY2hhcmFjdGVycw==</PHRASE>
<PHRASE Label="la_Pending" Module="Core" Type="1">UGVuZGluZw==</PHRASE>
@@ -1007,8 +1016,10 @@
<PHRASE Label="la_RequiredWarning" Module="Core" Type="1">Tm90IGFsbCByZXF1aXJlZCBmaWVsZHMgYXJlIGZpbGxlZC4gUGxlYXNlIGZpbGwgdGhlbSBmaXJzdC4=</PHRASE>
<PHRASE Label="la_review_perpage_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2U=</PHRASE>
<PHRASE Label="la_review_perpage_short_prompt" Module="Core" Type="1">Q29tbWVudHMgcGVyIFBhZ2UgKHNob3J0LWxpc3Qp</PHRASE>
+ <PHRASE Label="la_RevisionNumber" Module="Core" Type="1">UmV2aXNpb24gIyVz</PHRASE>
<PHRASE Label="la_rootcategory_name" Module="Core" Type="1">SG9tZQ==</PHRASE>
<PHRASE Label="la_SampleText" Module="Core" Type="1">U2FtcGxlIFRleHQ=</PHRASE>
+ <PHRASE Label="la_SavedAt" Module="Core" Type="1">c2F2ZWQgYXQgJXM=</PHRASE>
<PHRASE Label="la_SaveLogin" Module="Core" Type="1">U2F2ZSBVc2VybmFtZSBvbiBUaGlzIENvbXB1dGVy</PHRASE>
<PHRASE Label="la_Search" Module="Core" Type="1">U2VhcmNo</PHRASE>
<PHRASE Label="la_section_BasicPermissions" Module="Core" Type="1">QmFzaWMgUGVybWlzc2lvbnM=</PHRASE>
@@ -1264,7 +1275,9 @@
<PHRASE Label="la_title_EditingAgent" Module="Core" Type="1">RWRpdGluZyBBZ2VudA==</PHRASE>
<PHRASE Label="la_title_EditingBanRule" Module="Core" Type="1">RWRpdGluZyBCYW4gUnVsZQ==</PHRASE>
<PHRASE Label="la_title_EditingChangeLog" Module="Core" Type="1">RWRpdGluZyBDaGFuZ2VzIExvZw==</PHRASE>
+ <PHRASE Label="la_title_EditingContentAutosaved" Module="Core" Type="1">Q29udGVudCBFZGl0b3IgLSBBdXRvLXNhdmVkIGF0ICVz</PHRASE>
<PHRASE Label="la_title_EditingCountryState" Module="Core" Type="1">RWRpdGluZyBDb3VudHJ5L1N0YXRl</PHRASE>
+ <PHRASE Label="la_title_EditingDraft" Module="Core" Type="1">RWRpdGluZyBEcmFmdCAoJTIkcyk=</PHRASE>
<PHRASE Label="la_title_EditingEmailEvent" Module="Core" Type="1">RWRpdGluZyBFbWFpbCBFdmVudA==</PHRASE>
<PHRASE Label="la_title_EditingFile" Module="Core" Type="1">RWRpdGluZyBGaWxl</PHRASE>
<PHRASE Label="la_title_EditingMembership" Module="Core" Type="1">RWRpdGluZyBNZW1iZXJzaGlw</PHRASE>
@@ -1378,6 +1391,7 @@
<PHRASE Label="la_title_ViewingFormSubmission" Module="Core" Type="1">Vmlld2luZyBmb3JtIHN1Ym1pc3Npb24=</PHRASE>
<PHRASE Label="la_title_ViewingMailingList" Module="Core" Type="1">Vmlld2luZyBNYWlsaW5nIExpc3Q=</PHRASE>
<PHRASE Label="la_title_ViewingReply" Module="Core" Type="1">Vmlld2luZyBSZXBseQ==</PHRASE>
+ <PHRASE Label="la_title_ViewingRevision" Module="Core" Type="1">Vmlld2luZyBSZXZpc2lvbiAjJXMgKCVzKQ==</PHRASE>
<PHRASE Label="la_title_Visits" Module="Core" Type="1">VmlzaXRz</PHRASE>
<PHRASE Label="la_title_Website" Module="Core" Type="1">V2Vic2l0ZQ==</PHRASE>
<PHRASE Label="la_To" Module="Core" Type="1">dG8=</PHRASE>
@@ -1404,6 +1418,7 @@
<PHRASE Label="la_ToolTip_Deny" Module="Core" Type="1">RGVueQ==</PHRASE>
<PHRASE Label="la_ToolTip_Details" Module="Core" Type="1">RGV0YWlscw==</PHRASE>
<PHRASE Label="la_ToolTip_Disable" Module="Core" Type="1">RGlzYWJsZQ==</PHRASE>
+ <PHRASE Label="la_ToolTip_Discard" Module="Core" Type="1">RGlzY2FyZA==</PHRASE>
<PHRASE Label="la_ToolTip_Edit" Module="Core" Type="1">RWRpdA==</PHRASE>
<PHRASE Label="la_ToolTip_Edit_Current_Category" Module="Core" Type="1">RWRpdCBDdXJyZW50IFNlY3Rpb24=</PHRASE>
<PHRASE Label="la_ToolTip_Email_FrontOnly" Module="Core" Type="1">RnJvbnQtRW5kIE9ubHk=</PHRASE>
@@ -1411,6 +1426,7 @@
<PHRASE Label="la_ToolTip_Export" Module="Core" Type="1">RXhwb3J0</PHRASE>
<PHRASE Label="la_ToolTip_ExportLanguage" Module="Core" Type="1">RXhwb3J0IExhbmd1YWdl</PHRASE>
<PHRASE Label="la_ToolTip_HideMenu" Module="Core" Type="1">SGlkZSBNZW51</PHRASE>
+ <PHRASE Label="la_ToolTip_History" Module="Core" Type="1">SGlzdG9yeQ==</PHRASE>
<PHRASE Label="la_ToolTip_Home" Module="Core" Type="1">SG9tZQ==</PHRASE>
<PHRASE Label="la_ToolTip_Import" Module="Core" Type="1">SW1wb3J0</PHRASE>
<PHRASE Label="la_ToolTip_ImportLanguage" Module="Core" Type="1">SW1wb3J0IExhbmd1YWdl</PHRASE>
@@ -1444,9 +1460,11 @@
<PHRASE Label="la_ToolTip_Next" Module="Core" Type="1">TmV4dA==</PHRASE>
<PHRASE Label="la_ToolTip_Paste" Module="Core" Type="1">UGFzdGU=</PHRASE>
<PHRASE Label="la_ToolTip_Prev" Module="Core" Type="1">UHJldmlvdXM=</PHRASE>
+ <PHRASE Label="la_ToolTip_Preview" Module="Core" Type="1">UHJldmlldw==</PHRASE>
<PHRASE Label="la_ToolTip_PrimaryGroup" Module="Core" Type="1">U2V0IFByaW1hcnkgR3JvdXA=</PHRASE>
<PHRASE Label="la_ToolTip_Print" Module="Core" Type="1">UHJpbnQ=</PHRASE>
<PHRASE Label="la_ToolTip_ProcessQueue" Module="Core" Type="1">UHJvY2VzcyBRdWV1ZQ==</PHRASE>
+ <PHRASE Label="la_ToolTip_Publish" Module="Core" Type="1">UHVibGlzaA==</PHRASE>
<PHRASE Label="la_ToolTip_RebuildCategoryCache" Module="Core" Type="1">UmVidWlsZCBTZWN0aW9uIENhY2hl</PHRASE>
<PHRASE Label="la_ToolTip_RecalculatePriorities" Module="Core" Type="1">UmVjYWxjdWxhdGUgUHJpb3JpdGllcw==</PHRASE>
<PHRASE Label="la_ToolTip_Refresh" Module="Core" Type="1">UmVmcmVzaA==</PHRASE>
Index: install/install_data.sql
===================================================================
--- install/install_data.sql (revision 14853)
+++ install/install_data.sql (working copy)
@@ -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: install/install_schema.sql
===================================================================
--- install/install_schema.sql (revision 14853)
+++ install/install_schema.sql (working copy)
@@ -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: install/remove_schema.sql
===================================================================
--- install/remove_schema.sql (revision 14826)
+++ install/remove_schema.sql (working copy)
@@ -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: install/upgrades.php
===================================================================
--- install/upgrades.php (revision 14853)
+++ install/upgrades.php (working copy)
@@ -1836,6 +1836,7 @@
if ($mode == 'after') {
$this->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: install/upgrades.sql
===================================================================
--- install/upgrades.sql (revision 14853)
+++ install/upgrades.sql (working copy)
@@ -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: kernel/application.php
===================================================================
--- kernel/application.php (revision 14826)
+++ kernel/application.php (working copy)
@@ -951,17 +951,14 @@
}
}
elseif ( $this->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: kernel/constants.php
===================================================================
--- kernel/constants.php (revision 14853)
+++ kernel/constants.php (working copy)
@@ -152,6 +152,9 @@
define('PAGE_TYPE_VIRTUAL', 1);
define('PAGE_TYPE_TEMPLATE', 2);
+ define('CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL', 5 * 60); // 5 minutes
+ define('CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL', 20); // 20 seconds
+
define('reBEFORE', 1);
define('reAFTER', 2);
Index: kernel/db/db_event_handler.php
===================================================================
--- kernel/db/db_event_handler.php (revision 14855)
+++ kernel/db/db_event_handler.php (working copy)
@@ -697,7 +697,9 @@
$object->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: kernel/languages/phrases_cache.php
===================================================================
--- kernel/languages/phrases_cache.php (revision 14826)
+++ kernel/languages/phrases_cache.php (working copy)
@@ -143,6 +143,7 @@
if ( !isset($language_id) ) {
if ($this->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: units/categories/categories_config.php
===================================================================
--- units/categories/categories_config.php (revision 14826)
+++ units/categories/categories_config.php (working copy)
@@ -270,7 +270,7 @@
'-virtual' => '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: units/categories/categories_tag_processor.php
===================================================================
--- units/categories/categories_tag_processor.php (revision 14826)
+++ units/categories/categories_tag_processor.php (working copy)
@@ -1144,30 +1144,27 @@
function ContentBlock($params)
{
$num = getArrayValue($params, 'num');
- if (!$num) {
+ if ( !$num ) {
return 'NO CONTENT NUM SPECIFIED';
}
$page =& $this->_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 = '<link rel="stylesheet" href="' . $css_compressed . '" type="text/css" media="screen"/>' . "\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,8 +1398,30 @@
$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');
@@ -1409,6 +1429,9 @@
$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 = \'<img src="' . $js_url . '/../img/close_window15.gif" width="15" height="15" style="border-width: 0px;" alt="close"/><br/>\';' . "\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 .= '
+ <div>
+ <div id="cms-editing-notice">
+ <div class="top">
+ <a href="#" id="cms-close-editing-notice"></a>
+ <span prev_editors=""></span>
+ </div>
+ <div class="bottom"></div>
+ </div>
+
+ <div id="cms-revision-dropdown">
+ <div class="top"></div>
+ <div class="bottom"></div>
+ </div>
+ </div>
+
+ <div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '>
+ <div id="cms-revision-toolbar">
+ <script type="text/javascript">
+ var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/");
+ ' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );';
+
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) {
+ $edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );';
+ }
+
+ $edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs);
+
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) {
+ $edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs);
+ }
+
+ $edit_code .= $tabs . 'a_toolbar.Render();' . "\n";
+
+ $revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $revision kDBItem */
+
+ if ( !$revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("select");' . $tabs . 'a_toolbar.DisableButton("delete");' . $tabs . 'a_toolbar.DisableButton("preview");';
+ }
+
+ if ( $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("approve");';
+ }
+
+ if ( $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("decline");';
+ }
+
+ $publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true);
+
+ $edit_code .= substr($tabs, 0, -1) . '</script>
+
+ <div id="cms-current-revision-info">
+ <span class="revision-title"></span>
+ <div class="draft-saved"></div>
+ </div>
+
+ <a href="#" id="cms-close-toolbar"></a>
+ <div class="cms-clear"></div>
+ </div>
+
+ <a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a>
+ </div>' . "\n";
+ }
+
$edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">';
}
@@ -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 .= '<form method="POST" style="display: inline; margin: 0px" name="kf_revisions_'.$page->GetID().'" id="kf_revisions_'.$page->GetID().'" action="' . $page_admin_url . '">
+ <input type="hidden" name="revision" value="' . $this->Application->GetVar('revision', 0) . '"/>
+ </form>';
+ }
+
$edit_code .= '<form method="POST" style="display: inline; margin: 0px" name="kf_'.$page->GetID().'" id="kf_'.$page->GetID().'" action="'.$edit_url.'"></form>';
// 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: units/content/content_config.php
===================================================================
--- units/content/content_config.php (revision 14826)
+++ units/content/content_config.php (working copy)
@@ -30,8 +30,8 @@
),
'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,
@@ -48,10 +48,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 +63,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', 'min_len' => 0, 'max_len' => 65536,
'formatter' => 'kMultiLanguage', 'using_fck' => 1,
Index: units/content/content_eh.php
===================================================================
--- units/content/content_eh.php (revision 14826)
+++ units/content/content_eh.php (working copy)
@@ -36,4 +36,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 14826)
+++ units/helpers/helpers_config.php (working copy)
@@ -68,6 +68,7 @@
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'),
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: units/helpers/minifiers/minify_helper.php
===================================================================
--- units/helpers/minifiers/minify_helper.php (revision 14837)
+++ units/helpers/minifiers/minify_helper.php (working copy)
@@ -96,7 +96,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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageHelper extends kHelper {
+
+ /**
+ * Returns page info
+ *
+ * @param int $page_id
+ * @return Array
+ */
+ function getPageInfo($page_id)
+ {
+ list ($user_id, $history_permission) = $this->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: units/helpers/permissions_helper.php
===================================================================
--- units/helpers/permissions_helper.php (revision 14826)
+++ units/helpers/permissions_helper.php (working copy)
@@ -395,6 +395,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
@@ -410,12 +411,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) {
@@ -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: 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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageRevisionEventHandler extends kDBEventHandler {
+
+ /**
+ * Checks permissions of user
+ *
+ * @param kEvent $event
+ */
+ function CheckPermission(&$event)
+ {
+ if ( $event->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: 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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+class PageRevisionTagProcessor extends kDBTagProcessor {
+
+ function LastAutoSaveAgo($params)
+ {
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ $page_helper =& $this->Application->recallObject('PageHelper');
+ /* @var $page_helper PageHelper */
+
+ return $page_helper->getAgoTime( $object->GetDBField('AutoSavedOn') );
+ }
+}
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 @@
+<?php
+/**
+* @version $Id$
+* @package In-Portal
+* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
+* @license GNU/GPL
+* In-Portal is Open Source software.
+* This means that this software may have been modified pursuant
+* the GNU General Public License, and as distributed it includes
+* or is derivative of works licensed under the GNU General Public License
+* or other free or open source software licenses.
+* See http://www.in-portal.org/license for copyright notices and details.
+*/
+
+defined('FULL_PATH') or die('restricted access!');
+
+ $config = Array (
+ 'Prefix' => '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: units/structure/structure_config.php
===================================================================
--- units/structure/structure_config.php (revision 14826)
+++ 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),
'DirectLinkEnabled' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_Yes', 0 => 'la_No'), 'use_phrases' => 1,
page_revision_upgrade_fix.patch [^] (426 bytes) 2012-01-06 10:33
[Show Content]
Index: upgrades.php
===================================================================
--- upgrades.php (revision 14988)
+++ upgrades.php (working copy)
@@ -1924,7 +1924,7 @@
'PageId' => $page_id,
'RevisionNumber' => 1,
'IsDraft' => 0,
- 'FromRevisionNumber' => 0,
+ 'FromRevisionId' => 0,
'CreatedById' => USER_ROOT,
'CreatedOn' => adodb_mktime(),
'Status' => STATUS_ACTIVE,
setting_to_enable_page_revision_control.patch [^] (12,998 bytes) 2012-01-16 10:45
[Show Content]
Index: admin_templates/categories/edit_content.tpl
===================================================================
--- admin_templates/categories/edit_content.tpl (revision 15042)
+++ admin_templates/categories/edit_content.tpl (working copy)
@@ -73,32 +73,34 @@
}
);
- setInterval(
- function () {
- var $editor_instance = CKEDITOR.instances[ '<inp2:content_InputName field="Content"/>' ];
- $('#' + jq('<inp2:content_InputName field="Content"/>')).val( $editor_instance.getData() );
+ <inp2:m_if check="m_GetConfig" name="EnablePageContentRevisionControl">
+ setInterval(
+ function () {
+ var $editor_instance = CKEDITOR.instances[ '<inp2:content_InputName field="Content"/>' ];
+ $('#' + jq('<inp2:content_InputName field="Content"/>')).val( $editor_instance.getData() );
- submit_event_ajax(
- 'content', 'OnAutoSave', undefined,
- function ($autosave_string) {
- $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
- }
- );
- }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL"/> * 1000
- );
-
- setInterval(
- function () {
- submit_event_ajax(
- 'content', 'OnGetAutoSaveTime', undefined,
- function ($autosave_string) {
- if ( $autosave_string ) {
+ submit_event_ajax(
+ 'content', 'OnAutoSave', undefined,
+ function ($autosave_string) {
$('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
}
- }
- );
- }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL"/> * 1000
- );
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_UPDATE_INTERVAL"/> * 1000
+ );
+
+ setInterval(
+ function () {
+ submit_event_ajax(
+ 'content', 'OnGetAutoSaveTime', undefined,
+ function ($autosave_string) {
+ if ( $autosave_string ) {
+ $('#blue_bar').html( $autosave_title.replace('%s', $autosave_string) );
+ }
+ }
+ );
+ }, <inp2:m_GetConst name="CONTENT_LASTAUTOSAVE_REFRESH_INTERVAL"/> * 1000
+ );
+ </inp2:m_if>
</script>
<inp2:m_include t="incs/footer"/>
\ No newline at end of file
Index: install/english.lang
===================================================================
--- install/english.lang (revision 15042)
+++ install/english.lang (working copy)
@@ -157,6 +157,7 @@
<PHRASE Label="la_config_DefaultEmailRecipients" Module="Core" Type="1">RGVmYXVsdCBFLW1haWwgUmVjaXBpZW50cw==</PHRASE>
<PHRASE Label="la_config_DefaultRegistrationCountry" Module="Core" Type="1">RGVmYXVsdCBSZWdpc3RyYXRpb24gQ291bnRyeQ==</PHRASE>
<PHRASE Label="la_config_DefaultTrackingCode" Module="Core" Type="1">RGVmYXVsdCBBbmFseXRpY3MgVHJhY2tpbmcgQ29kZQ==</PHRASE>
+ <PHRASE Label="la_config_EnablePageContentRevisionControl" Module="Core" Type="1">RW5hYmxlIFJldmlzaW9uIENvbnRyb2wgZm9yIFNlY3Rpb24gQ29udGVudA==</PHRASE>
<PHRASE Label="la_config_error_template" Module="Core" Type="1">VGVtcGxhdGUgZm9yICJGaWxlIG5vdCBmb3VuZCAoNDA0KSIgRXJyb3I=</PHRASE>
<PHRASE Label="la_config_ExcludeTemplateSectionsFromSearch" Module="Core" Type="1">RXhjbHVkZSB0ZW1wbGF0ZSBiYXNlZCBTZWN0aW9ucyBmcm9tIFNlYXJjaCBSZXN1bHRzIChpZS4gVXNlciBSZWdpc3RyYXRpb24p</PHRASE>
<PHRASE Label="la_config_FilenameSpecialCharReplacement" Module="Core" Type="1">RmlsZW5hbWUgU3BlY2lhbCBDaGFyIFJlcGxhY2VtZW50</PHRASE>
Index: install/install_data.sql
===================================================================
--- install/install_data.sql (revision 15042)
+++ install/install_data.sql (working copy)
@@ -77,6 +77,7 @@
INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthUsername', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthUsername', 'text', '', '', 40.16, 0, 0, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthPassword', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthPassword', 'password', NULL, NULL, 40.17, 0, 0, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'HTTPAuthBypassIPs', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_HTTPAuthBypassIPs', 'text', '', '', 40.18, 0, 0, NULL);
+INSERT INTO SystemSettings VALUES(DEFAULT, 'EnablePageContentRevisionControl', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_EnablePageContentRevisionControl', 'checkbox', '', '', 40.19, 0, 0, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Server', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailserver', 'text', NULL, NULL, 50.01, 0, 1, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Port', NULL, 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailport', 'text', NULL, NULL, 50.02, 0, 1, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'Smtp_Authenticate', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsMailling', 'la_prompt_mailauthenticate', 'checkbox', NULL, NULL, 50.03, 0, 1, NULL);
Index: install/upgrades.sql
===================================================================
--- install/upgrades.sql (revision 15042)
+++ install/upgrades.sql (working copy)
@@ -2601,3 +2601,5 @@
INSERT INTO SystemSettings VALUES(DEFAULT, 'CKFinderLicenseName', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_CKFinderLicenseName', 'text', NULL, NULL, 80.03, 0, 0, NULL);
INSERT INTO SystemSettings VALUES(DEFAULT, 'CKFinderLicenseKey', '', 'In-Portal', 'in-portal:configure_advanced', 'la_section_Settings3rdPartyAPI', 'la_config_CKFinderLicenseKey', 'text', NULL, NULL, 80.04, 0, 0, NULL);
+
+INSERT INTO SystemSettings VALUES(DEFAULT, 'EnablePageContentRevisionControl', '0', 'In-Portal', 'in-portal:configure_advanced', 'la_section_SettingsAdmin', 'la_config_EnablePageContentRevisionControl', 'checkbox', '', '', 40.19, 0, 0, NULL);
Index: units/categories/categories_tag_processor.php
===================================================================
--- units/categories/categories_tag_processor.php (revision 15041)
+++ units/categories/categories_tag_processor.php (working copy)
@@ -1590,56 +1590,58 @@
<div class="top"></div>
<div class="bottom"></div>
</div>
- </div>
+ </div>';
- <div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '>
- <div id="cms-revision-toolbar">
- <script type="text/javascript">
- var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/");
- ' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );';
+ if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') ) {
+ $edit_code .= '<div id="cms-revision-toolbar-layer"' . ($toolbar_hidden ? ' style="top: -56px;"' : '') . '>
+ <div id="cms-revision-toolbar">
+ <script type="text/javascript">
+ var a_toolbar = new ToolBar(undefined, undefined, "' . $base_url . '#MODULE#/admin_templates/img/");
+ ' . $this->toolbarButton('select', 'la_ToolTip_Save', $tabs) . $this->toolbarButton('delete', 'la_ToolTip_Discard', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep1") );';
- if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) {
- $edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );';
- }
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.MODERATE', 0) ) {
+ $edit_code .= $this->toolbarButton('approve', 'la_ToolTip_Publish', $tabs) . $this->toolbarButton('decline', 'la_ToolTip_Decline', $tabs) . $tabs . 'a_toolbar.AddButton( new ToolBarSeparator("sep2") );';
+ }
- $edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs);
+ $edit_code .= $this->toolbarButton('preview', 'la_ToolTip_Preview', $tabs);
- if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) {
- $edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs);
- }
+ if ( $this->Application->CheckAdminPermission('CATEGORY.REVISION.HISTORY.VIEW', 0) ) {
+ $edit_code .= $this->toolbarButton('history', 'la_ToolTip_History', $tabs);
+ }
- $edit_code .= $tabs . 'a_toolbar.Render();' . "\n";
+ $edit_code .= $tabs . 'a_toolbar.Render();' . "\n";
- $revision =& $this->Application->recallObject('page-revision.current');
- /* @var $revision kDBItem */
+ $revision =& $this->Application->recallObject('page-revision.current');
+ /* @var $revision kDBItem */
- if ( !$revision->GetDBField('IsDraft') ) {
- $edit_code .= $tabs . 'a_toolbar.DisableButton("select");' . $tabs . 'a_toolbar.DisableButton("delete");' . $tabs . 'a_toolbar.DisableButton("preview");';
- }
+ if ( !$revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("select");' . $tabs . 'a_toolbar.DisableButton("delete");' . $tabs . 'a_toolbar.DisableButton("preview");';
+ }
- if ( $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
- $edit_code .= $tabs . 'a_toolbar.DisableButton("approve");';
- }
+ if ( $revision->GetDBField('Status') == STATUS_ACTIVE || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("approve");';
+ }
- if ( $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
- $edit_code .= $tabs . 'a_toolbar.DisableButton("decline");';
- }
+ if ( $revision->GetDBField('Status') == STATUS_DISABLED || $revision->GetDBField('IsLive') || $revision->GetDBField('IsDraft') ) {
+ $edit_code .= $tabs . 'a_toolbar.DisableButton("decline");';
+ }
- $publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true);
+ $publishing_tools = $this->Application->Phrase('la_btn_PublishingTools', false, true);
- $edit_code .= substr($tabs, 0, -1) . '</script>
+ $edit_code .= substr($tabs, 0, -1) . '</script>
- <div id="cms-current-revision-info">
- <span class="revision-title"></span>
- <div class="draft-saved"></div>
+ <div id="cms-current-revision-info">
+ <span class="revision-title"></span>
+ <div class="draft-saved"></div>
+ </div>
+
+ <a href="#" id="cms-close-toolbar"></a>
+ <div class="cms-clear"></div>
</div>
- <a href="#" id="cms-close-toolbar"></a>
- <div class="cms-clear"></div>
- </div>
-
- <a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a>
- </div>' . "\n";
+ <a href="#" id="cms-toggle-revision-toolbar"' . ($toolbar_hidden ? '' : ' class="opened"') . '><span>' . $publishing_tools . '</span></a>
+ </div>' . "\n";
+ }
}
$edit_code .= '<div class="cms-section-properties-btn-container">' . $edit_btn . '<div class="cms-btn-content">';
Index: units/content/content_eh.php
===================================================================
--- units/content/content_eh.php (revision 15049)
+++ units/content/content_eh.php (working copy)
@@ -178,7 +178,7 @@
$revision->Load( $object->GetDBField('RevisionId') );
- if ( !$revision->GetDBField('IsDraft') ) {
+ if ( $this->Application->ConfigValue('EnablePageContentRevisionControl') && !$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'),
Index: units/page_revisions/page_revision_eh.php
===================================================================
--- units/page_revisions/page_revision_eh.php (revision 15042)
+++ units/page_revisions/page_revision_eh.php (working copy)
@@ -37,7 +37,7 @@
/* @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);
+ $perm_status = $this->Application->CheckPermission('CATEGORY.REVISION.ADD', 0) || $this->Application->CheckPermission('CATEGORY.REVISION.ADD.PENDING', 0);
return $perm_helper->finalizePermissionCheck($event, $perm_status);
}
@@ -218,7 +218,7 @@
}
/**
- * Returns user, who are edting current page right now
+ * Returns user, who are editing current page right now
*
* @param kEvent $event
*/
sql_error_on_page_revision_delete.patch [^] (628 bytes) 2012-01-17 03:27
[Show Content]
Index: units/page_revisions/page_revisions_config.php
===================================================================
--- units/page_revisions/page_revisions_config.php (revision 15052)
+++ units/page_revisions/page_revisions_config.php (working copy)
@@ -43,7 +43,7 @@
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s
FROM %1$s
- JOIN ' . TABLE_PREFIX . '%3$sCategories c ON c.CategoryId = %1$s.PageId
+ LEFT JOIN ' . TABLE_PREFIX . '%3$sCategories c ON c.CategoryId = %1$s.PageId
LEFT JOIN ' . TABLE_PREFIX . 'Users created_by ON created_by.PortalUserId = %1$s.CreatedById'
),
|