From c84be1bcedeb3816ace5581fa71daa46ac7b7277 Mon Sep 17 00:00:00 2001 From: Fabrice Date: Wed, 16 Oct 2024 18:19:22 +0200 Subject: [PATCH] Move patches && remove welcome page for prod --- Dockerfile | 56 +- README.md | 4 +- .../server/0001-fix-licences.patch | 17 + patches/web-apps-mobile/cell/patch.jsx | 558 +++++++++++++++ patches/web-apps-mobile/slide/patch.jsx | 670 ++++++++++++++++++ patches/web-apps-mobile/word/patch.jsx | 640 +++++++++++++++++ .../web-apps/0001-add-mobile.patch | 14 + .../img/AGPLv3_Logo.svg.png | Bin favicon.ico => welcome/img/favicon.ico | Bin ws-logo.png => welcome/img/ws-logo.png | Bin welcome.html => welcome/index.html | 0 11 files changed, 1930 insertions(+), 29 deletions(-) rename server.patch => patches/server/0001-fix-licences.patch (87%) create mode 100644 patches/web-apps-mobile/cell/patch.jsx create mode 100644 patches/web-apps-mobile/slide/patch.jsx create mode 100644 patches/web-apps-mobile/word/patch.jsx rename web-apps.patch => patches/web-apps/0001-add-mobile.patch (76%) rename AGPLv3_Logo.svg.png => welcome/img/AGPLv3_Logo.svg.png (100%) rename favicon.ico => welcome/img/favicon.ico (100%) rename ws-logo.png => welcome/img/ws-logo.png (100%) rename welcome.html => welcome/index.html (100%) diff --git a/Dockerfile b/Dockerfile index fe01c9c..9f4146e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,18 +20,14 @@ RUN apt-get update && \ echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ apt-get update && \ apt-get install -y ${build_deps} && \ - npm install -g pkg grunt grunt-cli - - -RUN apt-get clean && rm -rf /var/lib/apt/lists/* + npm install -g pkg grunt grunt-cli && \ + apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN node -v RUN npm -v WORKDIR /build - - ## Clone FROM setup-stage as clone-stage @@ -44,50 +40,56 @@ RUN git config --global advice.detachedHead false && \ git clone --quiet --branch $tag --depth 1 https://github.com/ONLYOFFICE/web-apps.git /build/web-apps +# Patch Server +COPY patches/server /build/server/patches +# Needs a fake author to allow commits +RUN git config --global user.email "contact@workstreams.ch" && \ + git config --global user.name "WS-Bot" && \ + cd /build/server && \ + git am --ignore-whitespace patches/*.patch && \ + make -COPY server.patch /build/ -COPY web-apps.patch /build/ - -RUN cd /build/server && git apply /build/server.patch -RUN cd /build/web-apps && git apply /build/web-apps.patch ## Build FROM clone-stage as build-stage # build server with license checks patched WORKDIR /build/server -RUN make -RUN pkg /build/build_tools/out/linux_64/onlyoffice/documentserver/server/FileConverter --targets=node16-linux -o /build/converter -RUN pkg /build/build_tools/out/linux_64/onlyoffice/documentserver/server/DocService --targets=node16-linux --options max_old_space_size=4096 -o /build/docservice +RUN make && \ + pkg /build/build_tools/out/linux_64/onlyoffice/documentserver/server/FileConverter --targets=node16-linux -o /build/converter && \ + pkg /build/build_tools/out/linux_64/onlyoffice/documentserver/server/DocService --targets=node16-linux --options max_old_space_size=4096 -o /build/docservice # build web-apps with mobile editing -WORKDIR /build/web-apps/build -RUN npm install -RUN grunt --addon=web-apps-mobile +RUN cd /build/web-apps/build && npm install +COPY patches/web-apps /build/web-apps/patches +COPY patches/web-apps-mobile /build/web-apps-mobile -RUN ls -l /build/web-apps/deploy/web-apps/apps/documenteditor/mobile/ -RUN ls -l /build/web-apps/deploy/web-apps/apps/presentationeditor/mobile/ -RUN ls -l /build/web-apps/deploy/web-apps/apps/spreadsheeteditor/mobile/ +RUN cd /build/web-apps && \ + git am --ignore-whitespace patches/*.patch && \ + cd /build/web-apps/build && grunt --addon=web-apps-mobile ## Final image FROM onlyoffice/documentserver:${product_version}.${build_number} ARG oo_root +ARG COMPANY_NAME=onlyoffice +ARG PRODUCT_NAME=documentserver +ARG DS_SUPERVISOR_CONF=/etc/supervisor/conf.d/ds.conf + +# Remove all documentserver-example data +RUN rm -rf /var/www/$COMPANY_NAME/$PRODUCT_NAME-example \ + && rm -rf /etc/$COMPANY_NAME/$PRODUCT_NAME-example \ + && rm -f $DS_SUPERVISOR_CONF \ + && rm -f /etc/nginx/includes/ds-example.conf \ + && ln -s /etc/$COMPANY_NAME/$PRODUCT_NAME/supervisor/ds.conf $DS_SUPERVISOR_CONF # server COPY --from=build-stage --chown=ds:ds /build/converter ${oo_root}/server/FileConverter/converter COPY --from=build-stage --chown=ds:ds /build/docservice ${oo_root}/server/DocService/docservice - # web-apps COPY --from=build-stage --chown=ds:ds /build/web-apps/deploy/web-apps/apps/documenteditor/mobile ${oo_root}/web-apps/apps/documenteditor/mobile COPY --from=build-stage --chown=ds:ds /build/web-apps/deploy/web-apps/apps/presentationeditor/mobile ${oo_root}/web-apps/apps/presentationeditor/mobile COPY --from=build-stage --chown=ds:ds /build/web-apps/deploy/web-apps/apps/spreadsheeteditor/mobile ${oo_root}/web-apps/apps/spreadsheeteditor/mobile COPY local-production-linux.json /etc/onlyoffice/documentserver/local-production-linux.json - -# Replace welcome page -COPY welcome.html ${oo_root}/web-apps/welcome/index.html -COPY favicon.ico ${oo_root}/web-apps/welcome/favicon.ico -COPY ws-logo.png ${oo_root}/web-apps/welcome/ws-logo.png -COPY AGPLv3_Logo.svg.png ${oo_root}/web-apps/welcome/AGPLv3_Logo.svg.png \ No newline at end of file diff --git a/README.md b/README.md index 1d0ef53..56f3c71 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ git clone --recursive https://git2.workstreams.ch/workstreams/wsoffice Create a Git patch for each repository ``` cd DocumentServer/server/ -git diff > ../../server.patch +git format-patch -1 -o ../../patches/server/ cd DocumentServer/web-apps/ -git diff > ../../web-apps.patch +git format-patch -1 -o ../../patches/wed-apps/ ``` diff --git a/server.patch b/patches/server/0001-fix-licences.patch similarity index 87% rename from server.patch rename to patches/server/0001-fix-licences.patch index 830a9b3..90c3534 100644 --- a/server.patch +++ b/patches/server/0001-fix-licences.patch @@ -1,3 +1,17 @@ +From fd17b0f68640cb90b81f516af52ab71e0242d410 Mon Sep 17 00:00:00 2001 +From: Fabrice +Date: Mon, 14 Oct 2024 16:57:02 +0200 +Subject: [PATCH] fix licences + +--- + Common/sources/commondefines.js | 4 ++-- + Common/sources/constants.js | 6 +++--- + Common/sources/license.js | 20 ++++++++++---------- + FileConverter/package.json | 3 ++- + Makefile | 3 ++- + package.json | 6 +++--- + 6 files changed, 22 insertions(+), 20 deletions(-) + diff --git a/Common/sources/commondefines.js b/Common/sources/commondefines.js index fc8756b9..c1a9a7ff 100644 --- a/Common/sources/commondefines.js @@ -136,3 +150,6 @@ index 78c3c50d..36593a55 100644 -} +} \ No newline at end of file +-- +2.30.2 + diff --git a/patches/web-apps-mobile/cell/patch.jsx b/patches/web-apps-mobile/cell/patch.jsx new file mode 100644 index 0000000..549c82b --- /dev/null +++ b/patches/web-apps-mobile/cell/patch.jsx @@ -0,0 +1,558 @@ +import React, {Fragment} from 'react'; +import {Link} from 'framework7-react'; +import { Device } from '../../web-apps/apps/common/mobile/utils/device'; + +const EditorUIController = () => { + return null +}; + +EditorUIController.isSupportEditFeature = () => { + return true; +}; + +/** + +This is the file where the magic happens. +Try to figure out a working version from a both the old commits +and my hunches from hacking around in the old webapp editor + +https://github.com/ONLYOFFICE/web-apps/commit/7c72e8cb1b3e06ac72b0e7156d33b8a8d3206dc4 +*/ + +EditorUIController.toolbarOptions = { + // This implementation is likely incomplete, since wsProps, focusOn, isShapeLocked are currently ununsed + // and wsProps was specifically added to the options given to getEditOptions in order to handle protection + // https://github.com/ONLYOFFICE/web-apps/commit/1508b2ee641e9f8e633628dfe7e76bfe8a2f82b2#diff-9358b4112e282a95c6f9568c509cfa89d866b92701dfd5d347349ad85971e53eR31 + // wsProps contains severals properties that are true if specific protection is enabled + getEditOptions: (options) => { + // console.log('EditorUIController.toolbarOptions.getEditOptions' ,options); + return ( + + options.onEditClick()}> + options.onAddClick()}> + + ); + }, + + getUndoRedo: (options) => { + return ( + + options.onUndoClick()}> + options.onRedoClick()}> + + ); + } +} + +EditorUIController.initCellInfo = (props) => { + // console.log('EditorUIController.initCellInfo', props); + + const storeFocusObjects = props.storeFocusObjects; + const storeCellSettings = props.storeCellSettings; + const storeTextSettings = props.storeTextSettings; + const storeChartSettings = props.storeChartSettings; + const api = Common.EditorApi.get(); + + storeFocusObjects.intf = EditorUIController.Intf(storeFocusObjects); + + api.asc_registerCallback('asc_onSelectionChanged', cellInfo => { + // console.log("asc_onSelectionChanged",cellInfo); + // console.log('storeFocusObjects', { + // 'isLocked': storeFocusObjects.isLocked, + // 'isLockedText': storeFocusObjects.isLockedText, + // 'isLockedShaped': storeFocusObjects.isLockedShaped, + // 'editFormulaMode': storeFocusObjects.editFormulaMode, + // 'isEditCell': storeFocusObjects.isEditCell, + // 'functionDisabled': storeFocusObjects.functionDisabled + // }); + + // TODO Not sure if good idea or not but maybe we should check if cell is locked + // In that case, get the protected ranges and see if we should allow to select + // that cell. + // const api = Common.EditorApi.get(); + + // console.log({ + // 'api.asc_isProtectedSheet': api.asc_isProtectedSheet(), + // 'api.asc_checkLockedCells': api.asc_checkLockedCells(), + // 'shouldCheckProtectedRanges': api.asc_isProtectedSheet() && api.asc_checkLockedCells(), + // 'iscelllocked': api.asc_isProtectedSheet() && api.asc_checkLockedCells() ? api.asc_checkProtectedRange() : false + // }); + + // This changes the type of the selected focus to show different edit views. + // The edit views needs the focusOn value to be set. + // Since this change was made after the mobile editor has been made private, I'm not + // sure if this is the way to know if the selection is a cell or an object but this + // seems to work. this old commit shows how changeFocus used to work: + // https://github.com/ONLYOFFICE/web-apps/commit/050c03096243448b040fc676cd350c090a1a8c59 + const isObject = cellInfo.asc_getSelectionType() !== Asc.c_oAscSelectionType.RangeCells; + storeFocusObjects.changeFocus(isObject); + + storeFocusObjects.resetCellInfo(cellInfo); + storeCellSettings.initCellSettings(cellInfo); + storeTextSettings.initTextSettings(cellInfo); + + let selectedObjects = Common.EditorApi.get().asc_getGraphicObjectProps(); + + if(selectedObjects.length) { + storeFocusObjects.resetFocusObjects(selectedObjects); + + // Chart Settings + + if (storeFocusObjects.chartObject) { + storeChartSettings.updateChartStyles(api.asc_getChartPreviews(storeFocusObjects.chartObject.get_ChartProperties().getType())); + } + } + }); +} + +EditorUIController.initFonts = (props) => { + // console.log('EditorUIController.initFonts', storeTextSettings); + + const storeCellSettings = props.storeCellSettings; + const storeTextSettings = props.storeTextSettings; + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorFonts', (fonts, select) => { + // console.log('asc_onInitEditorFonts', fonts, select); + storeCellSettings.initEditorFonts(fonts, select); + storeTextSettings.initEditorFonts(fonts, select); + }); + + // Maybe this go here ? + api.asc_registerCallback('asc_onEditorSelectionChanged', fontObj => { + // console.log(fontObj) + storeCellSettings.initFontInfo(fontObj); + storeTextSettings.initFontInfo(fontObj); + }); +}; +EditorUIController.initEditorStyles = (storeCellSettings) => { + // console.log('EditorUIController.initEditorStyles', storeCellSettings); + + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorStyles', styles => { + storeCellSettings.initCellStyles(styles); + }); +} + +EditorUIController.initThemeColors = () => { + // console.log('EditorUIController.initThemeColors'); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onSendThemeColors', (colors, standart_colors) => { + // console.log('asc_onSendThemeColors', colors, standart_colors); + Common.Utils.ThemeColor.setColors(colors, standart_colors); + }); +} + +EditorUIController.ContextMenu = { + // https://github.com/ONLYOFFICE/web-apps/commit/588a7f03863ebf9ec3251a36decc8b4e280dda59 + mapMenuItems: (contextMenu) => { + // console.log('EditorUIController.ContextMenu.mapMenuItems', contextMenu); + + if ( !Common.EditorApi ) return []; + + const { t } = contextMenu.props; + const _t = t("ContextMenu", { returnObjects: true }); + + const { canViewComments } = contextMenu.props; + + const api = Common.EditorApi.get(); + const cellinfo = api.asc_getCellInfo(); + + const itemsIcon = []; + const itemsText = []; + + let iscellmenu, isrowmenu, iscolmenu, isallmenu, ischartmenu, isimagemenu, istextshapemenu, isshapemenu, istextchartmenu; + let iscelllocked = cellinfo.asc_getLocked(); + const seltype = cellinfo.asc_getSelectionType(); + const xfs = cellinfo.asc_getXfs(); + const isComments = cellinfo.asc_getComments().length > 0; //prohibit adding multiple comments in one cell; + + // Check if the individual cell is locked + // This code is taken from ./web-apps/apps/spreadsheeteditor/main/app/controller/Main.js + // It seems that api.asc_checkProtectedRange() returns null if the cell is locked. + iscelllocked = api.asc_isProtectedSheet() && api.asc_checkLockedCells() ? api.asc_checkProtectedRange() : false; + iscelllocked = iscelllocked === null ? true : false; + + const { isDisconnected } = contextMenu.props; + + switch (seltype) { + case Asc.c_oAscSelectionType.RangeCells: iscellmenu = true; break; + case Asc.c_oAscSelectionType.RangeRow: isrowmenu = true; break; + case Asc.c_oAscSelectionType.RangeCol: iscolmenu = true; break; + case Asc.c_oAscSelectionType.RangeMax: isallmenu = true; break; + case Asc.c_oAscSelectionType.RangeImage: isimagemenu = true; break; + case Asc.c_oAscSelectionType.RangeShape: isshapemenu = true; break; + case Asc.c_oAscSelectionType.RangeChart: ischartmenu = true; break; + case Asc.c_oAscSelectionType.RangeChartText: istextchartmenu = true; break; + case Asc.c_oAscSelectionType.RangeShapeText: istextshapemenu = true; break; + } + + if (!iscelllocked && (isimagemenu || isshapemenu || ischartmenu || istextshapemenu || istextchartmenu)) { + api.asc_getGraphicObjectProps().every((object) => { + if (object.asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) { + iscelllocked = object.asc_getObjectValue().asc_getLocked(); + } + return !iscelllocked; + }); + } + + if (iscelllocked || api.isCellEdited) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + + } else { + itemsIcon.push({ + event: 'cut', + icon: 'icon-cut' + }); + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + itemsIcon.push({ + event: 'paste', + icon: 'icon-paste' + }); + itemsText.push({ + caption: _t.menuDelete, + event: 'del' + }); + + if (isimagemenu || isshapemenu || ischartmenu || + istextshapemenu || istextchartmenu) { + itemsText.push({ + caption: _t.menuEdit, + event: 'edit' + }); + } else { + if (iscolmenu || isrowmenu) { + itemsText.push({ + caption: _t.menuHide, + event: 'hide' + }); + itemsText.push({ + caption: _t.menuShow, + event: 'show' + }); + } else if (iscellmenu) { + if (!iscelllocked) { + itemsText.push({ + caption: _t.menuCell, + event: 'edit' + }); + } + + if (cellinfo.asc_getMerge() == Asc.c_oAscMergeOptions.None) { + itemsText.push({ + caption: _t.menuMerge, + event: 'merge' + }); + } + + if (cellinfo.asc_getMerge() == Asc.c_oAscMergeOptions.Merge) { + itemsText.push({ + caption: _t.menuUnmerge, + event: 'unmerge' + }); + } + + itemsText.push( + xfs.asc_getWrapText() ? + { + caption: _t.menuUnwrap, + event: 'unwrap' + } : + { + caption: _t.menuWrap, + event: 'wrap' + }); + + if (cellinfo.asc_getHyperlink() && !cellinfo.asc_getMultiselect()) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + itemsText.push({ + caption: _t.menuEditLink, + event: 'editlink' + }); + } else if (!cellinfo.asc_getHyperlink() && !cellinfo.asc_getMultiselect() && + !cellinfo.asc_getLockText() && !!cellinfo.asc_getText()) { + itemsText.push({ + caption: _t.menuAddLink, + event: 'addlink' + }); + } + } + + itemsText.push({ + caption: api.asc_getSheetViewSettings().asc_getIsFreezePane() ? _t.menuUnfreezePanes : _t.menuFreezePanes, + event: 'freezePanes' + }); + + } + + if (canViewComments) { + if (isComments) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } else if (iscellmenu) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + } + + // The below is guesswork on how Onlyoffice should handle itself with locked cells. + if(iscelllocked) { + if (iscellmenu && cellinfo.asc_getHyperlink()) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + } + + const {isRestrictedEdit, canViewComments, canCoAuthoring, canComments, isResolvedComments, isVersionHistoryMode} = contextMenu.props; + const comments = cellinfo.asc_getComments(); //prohibit adding multiple comments in one cell; + + if(!isDisconnected && !isVersionHistoryMode) { + if (canViewComments && comments && comments.length && ((!isSolvedComment && !isResolvedComments) || isResolvedComments)) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + if (iscellmenu && !api.isCellEdited && isRestrictedEdit && canCoAuthoring && canComments && comments && comments.length<1) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + } + + if ( Device.phone && itemsText.length > 2 ) { + contextMenu.extraItems = itemsText.splice(2,itemsText.length, { + caption: _t.menuMore, + event: 'showActionSheet' + }); + } + + return itemsIcon.concat(itemsText); + } , + // https://github.com/ONLYOFFICE/web-apps/commit/588a7f03863ebf9ec3251a36decc8b4e280dda59 + handleMenuItemClick: (contextMenu, action) => { + // console.log('EditorUIController.ContextMenu.handleMenuItemClick', contextMenu, action); + + const api = Common.EditorApi.get(); + const info = api.asc_getCellInfo(); + switch (action) { + case 'addcomment': + Common.Notifications.trigger('addcomment'); + return true; + case 'del': + api.asc_emptyCells(Asc.c_oAscCleanOptions.All); + return true; + case 'wrap': + api.asc_setCellTextWrap(true); + return true; + case 'unwrap': + api.asc_setCellTextWrap(false); + return true; + case 'edit': + setTimeout(() => { + contextMenu.props.openOptions('edit'); + }, 0); + return true; + case 'merge': + if (api.asc_mergeCellsDataLost(Asc.c_oAscMergeOptions.Merge)) { + setTimeout(() => { + f7.dialog.confirm(_t.warnMergeLostData, _t.notcriticalErrorTitle, () => { + api.asc_mergeCells(Asc.c_oAscMergeOptions.Merge); + }); + }, 0); + } else { + api.asc_mergeCells(Asc.c_oAscMergeOptions.Merge); + } + return true; + case 'unmerge': + api.asc_mergeCells(Asc.c_oAscMergeOptions.None); + return true; + case 'hide': + api[info.asc_getSelectionType() == Asc.c_oAscSelectionType.RangeRow ? 'asc_hideRows' : 'asc_hideColumns'](); + return true; + case 'show': + api[info.asc_getSelectionType() == Asc.c_oAscSelectionType.RangeRow ? 'asc_showRows' : 'asc_showColumns'](); + return true; + case 'addlink': + setTimeout(() => { + contextMenu.props.openOptions('add', 'link'); + }, 400) + return true; + case 'editlink': + setTimeout(() => { + contextMenu.props.openOptions('edit', 'link'); + }, 400) + return true; + case 'freezePanes': + api.asc_freezePane(); + break; + } + + return false; + }, +}; + +// This objects holds the mysterious functions in focusObjects.js inside the intf object +// These for now are based on the latest valid implementation from the mobile editor +// https://github.com/ONLYOFFICE/web-apps/commit/7c72e8cb1b3e06ac72b0e7156d33b8a8d3206dc4#diff-a9931472dbd29941dda676251382f80122ff2f08aadc849684e08b505d174bf2 +EditorUIController.Intf = function (storeFocusObjects) { + var obj = {}; + + obj.getSelections = () => { + const _selections = []; + + let isCell, isRow, isCol, isAll, isChart, isImage, isTextShape, isShape, isTextChart, + selType = storeFocusObjects._cellInfo.asc_getSelectionType(), + isObjLocked = false; + + switch (selType) { + case Asc.c_oAscSelectionType.RangeCells: isCell = true; break; + case Asc.c_oAscSelectionType.RangeRow: isRow = true; break; + case Asc.c_oAscSelectionType.RangeCol: isCol = true; break; + case Asc.c_oAscSelectionType.RangeMax: isAll = true; break; + case Asc.c_oAscSelectionType.RangeImage: isImage = true; break; + case Asc.c_oAscSelectionType.RangeShape: isShape = true; break; + case Asc.c_oAscSelectionType.RangeChart: isChart = true; break; + case Asc.c_oAscSelectionType.RangeChartText:isTextChart = true; break; + case Asc.c_oAscSelectionType.RangeShapeText: isTextShape = true; break; + } + + if (isImage || isShape || isChart) { + isImage = isShape = isChart = false; + let has_chartprops = false; + let selectedObjects = Common.EditorApi.get().asc_getGraphicObjectProps(); + + for (let i = 0; i < selectedObjects.length; i++) { + if (selectedObjects[i].asc_getObjectType() == Asc.c_oAscTypeSelectElement.Image) { + const elValue = selectedObjects[i].asc_getObjectValue(); + isObjLocked = isObjLocked || elValue.asc_getLocked(); + const shapeProps = elValue.asc_getShapeProperties(); + + if (shapeProps) { + if (shapeProps.asc_getFromChart()) { + isChart = true; + } else { + isShape = true; + + } + } else if (elValue.asc_getChartProperties()) { + isChart = true; + has_chartprops = true; + } else { + isImage = true; + } + } + } + } else if (isTextShape || isTextChart) { + const selectedObjects = Common.EditorApi.get().asc_getGraphicObjectProps(); + let isEquation = false; + + for (var i = 0; i < selectedObjects.length; i++) { + const elType = selectedObjects[i].asc_getObjectType(); + if (elType == Asc.c_oAscTypeSelectElement.Image) { + const value = selectedObjects[i].asc_getObjectValue(); + isObjLocked = isObjLocked || value.asc_getLocked(); + } else if (elType == Asc.c_oAscTypeSelectElement.Paragraph) { + } else if (elType == Asc.c_oAscTypeSelectElement.Math) { + isEquation = true; + } + } + } + if (isChart || isTextChart) { + _selections.push('chart'); + + if (isTextChart) { + _selections.push('text'); + } + } else if ((isShape || isTextShape) && !isImage) { + _selections.push('shape'); + + if (isTextShape) { + _selections.push('text'); + } + } else if (isImage) { + _selections.push('image'); + + if (isShape) { + _selections.push('shape'); + } + } else { + _selections.push('cell'); + + if (storeFocusObjects._cellInfo.asc_getHyperlink()) { + _selections.push('hyperlink'); + } + } + return _selections; + } + + obj.getShapeObject = () => { + const shapes = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Image) { + if (object.get_ObjectValue() && object.get_ObjectValue().get_ShapeProperties()) { + shapes.push(object); + } + } + } + if (shapes.length > 0) { + const object = shapes[shapes.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getImageObject = () => { + const images = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Image) { + images.push(object); + } + } + if (images.length > 0) { + const object = images[images.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getChartObject = () => { + const charts = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Image) { + if (object.get_ObjectValue() && object.get_ObjectValue().get_ChartProperties()) { + charts.push(object); + } + } + } + if (charts.length > 0) { + const object = charts[charts.length - 1]; // get top table + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + return obj; +} + +export default EditorUIController; diff --git a/patches/web-apps-mobile/slide/patch.jsx b/patches/web-apps-mobile/slide/patch.jsx new file mode 100644 index 0000000..6f567a3 --- /dev/null +++ b/patches/web-apps-mobile/slide/patch.jsx @@ -0,0 +1,670 @@ +import React, {Fragment} from 'react'; +import {Link} from 'framework7-react'; +import { Device } from '../../web-apps/apps/common/mobile/utils/device'; +import { + AddCommentController, + EditCommentController, +} from "../../web-apps/apps/common/mobile/lib/controller/collaboration/Comments"; + +const EditorUIController = () => { + return null +}; + +EditorUIController.isSupportEditFeature = () => { + return true; +}; + +/** + +This is the file where the magic happens. +Try to figure out a working version from the old commits +and my hunches from hacking around in the old webapp editor +*/ + + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/0277ac930611db0637100a2c5e6891ea495610a8#diff-755ea3df479f23211982e28466b363be9df22cb92f4a5cf9126ec9e227d6909bL66-L72 +EditorUIController.getToolbarOptions = (options) => { + // console.log('EditorUIController.getToolbarOptions' ,options); + return ( + + options.onEditClick()}> + options.onAddClick()}> + + ); +}; + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/0277ac930611db0637100a2c5e6891ea495610a8 +EditorUIController.getUndoRedo = (options) => { + // console.log('EditorUIController.getUndoRedo', options); + return ( + + options.onUndoClick()}> + options.onRedoClick()}> + + ); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/45a9058da461a9da67ccb389faad08bce8cbda55#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL307-L308 +EditorUIController.getEditCommentControllers = () => { + // console.log('EditorUIController.getEditCommentControllers'); + return ( + + + + + ); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL233-L259 +EditorUIController.initFonts = (storeTextSettings) => { + // console.log('EditorUIController.initFonts', storeTextSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorFonts', (fonts, select) => { + // console.log('asc_onInitEditorFonts', fonts, select); + storeTextSettings.initEditorFonts(fonts, select); + }); + api.asc_registerCallback('asc_onFontFamily', (font) => { + // console.log('asc_onFontFamily', font); + storeTextSettings.resetFontName(font); + }); + api.asc_registerCallback('asc_onFontSize', (size) => { + // console.log('asc_onFontSize', size); + storeTextSettings.resetFontSize(size); + }); + api.asc_registerCallback('asc_onBold', (isBold) => { + // console.log('asc_onBold', isBold); + storeTextSettings.resetIsBold(isBold); + }); + api.asc_registerCallback('asc_onItalic', (isItalic) => { + // console.log('asc_onItalic', isItalic); + storeTextSettings.resetIsItalic(isItalic); + }); + api.asc_registerCallback('asc_onUnderline', (isUnderline) => { + // console.log('asc_onUnderline', isUnderline); + storeTextSettings.resetIsUnderline(isUnderline); + }); + api.asc_registerCallback('asc_onStrikeout', (isStrikeout) => { + // console.log('asc_onStrikeout', isStrikeout); + storeTextSettings.resetIsStrikeout(isStrikeout); + }); +}; + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL210-L223 +EditorUIController.initEditorStyles = (storeSlideSettings) => { + // console.log('EditorUIController.initEditorStyles', storeSlideSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorStyles', themes => { + // themes is an array of array with index 0 representing defaultThemes and 1 representing documentThemes + // Inspired by what's done in /view/edit/EditSlide.jsx + let defaultThemes = themes[0]; + let docThemes = themes[1]; + let arrayThemes = []; + + defaultThemes.forEach(function(theme, index) { + arrayThemes.push({ + themeId : theme.get_Index(), + tip : theme.get_Name(), + offsety : index * 40 + }); + }); + docThemes.forEach(function(theme) { + arrayThemes.push({ + imageUrl: theme.get_Image(), + themeId : theme.get_Index(), + tip : theme.get_Name(), + offsety : 0 + }); + }); + + storeSlideSettings.addArrayThemes(arrayThemes); + }); + + api.asc_registerCallback('asc_onUpdateThemeIndex', themeId => { + // console.log(themeId); + storeSlideSettings.changeSlideThemeIndex(themeId); + }); + + api.asc_registerCallback('asc_onUpdateLayout', layouts => { + // console.log(layouts); + storeSlideSettings.addArrayLayouts(layouts); + }); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL202-L208 +EditorUIController.initFocusObjects = (storeFocusObjects) => { + storeFocusObjects.intf = EditorUIController.Intf(storeFocusObjects); + // console.log('EditorUIController.initFocusObjects', storeFocusObjects); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onFocusObject', objects => { + // console.log('asc_onFocusObject', objects); + storeFocusObjects.resetFocusObjects(objects); + }); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL318-L322 +EditorUIController.initTableTemplates = (storeTableSettings) => { + // console.log('EditorUIController.initTableTemplates', storeTableSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitTableTemplates', (templates) => { + // console.log('asc_onInitTableTemplates', templates); + storeTableSettings.initTableTemplates(templates); + }); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL225-L227 +EditorUIController.initThemeColors = () => { + // console.log('EditorUIController.initThemeColors'); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onSendThemeColors', (colors, standart_colors) => { + // console.log('asc_onSendThemeColors', colors, standart_colors); + Common.Utils.ThemeColor.setColors(colors, standart_colors); + }); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/45a9058da461a9da67ccb389faad08bce8cbda55#diff-1d89c904c6cd5147fb6942c09c4d12a2f3a3200a61b3e29b1a42a1263e57848bL264-L270 +EditorUIController.updateChartStyles = (storeChartSettings, storeFocusObjects) => { + // console.log('EditorUIController.updateChartStyles', storeChartSettings, storeFocusObjects); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onUpdateChartStyles', () => { + // console.log('asc_onUpdateChartStyles', storeFocusObjects.chartObject); + if (storeFocusObjects.chartObject && storeFocusObjects.chartObject.get_ChartProperties()) { + // console.log('we in'); + storeChartSettings.updateChartStyles(api.asc_getChartPreviews(storeFocusObjects.chartObject.get_ChartProperties().getType())); + } + }); +} + +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-f711cbc9449c97e0d6394cf70e9c04eeca1d9e520a1f2e7b390b1a5bc5acd82d +EditorUIController.ContextMenu = { + mapMenuItems: (contextMenu) => { + // console.log('EditorUIController.ContextMenu.mapMenuItems', contextMenu); + + if ( !Common.EditorApi ) return []; + + const { t } = contextMenu.props; + const _t = t("ContextMenu", { returnObjects: true }); + + const { isEdit, canViewComments, canReview, isDisconnected } = contextMenu.props; + + const { isViewer, isProtected } = contextMenu.props; + // This probably needs to be refired + if(!isEdit || isViewer || isProtected) + return EditorUIController.ContextMenu.mapMenuItemsReadOnly(contextMenu); + + const api = Common.EditorApi.get(); + const stack = api.getSelectedElements(); + const canCopy = api.can_CopyCut(); + + let itemsIcon = [], + itemsText = []; + + if ( canCopy ) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + } + + if ( canViewComments && contextMenu.isComments && !isEdit ) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + let isText = false, + isTable = false, + isImage = false, + isChart = false, + isShape = false, + isLink = false, + lockedText = false, + lockedTable = false, + lockedImage = false, + lockedHeader = false; + + stack.forEach(item => { + const objectType = item.get_ObjectType(), + objectValue = item.get_ObjectValue(); + + if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { + lockedHeader = objectValue.get_Locked(); + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { + lockedText = objectValue.get_Locked(); + isText = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Image ) { + lockedImage = objectValue.get_Locked(); + if ( objectValue && objectValue.get_ChartProperties() ) { + isChart = true; + } else + if ( objectValue && objectValue.get_ShapeProperties() ) { + isShape = true; + } else { + isImage = true; + } + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Table ) { + lockedTable = objectValue.get_Locked(); + isTable = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { + isLink = true; + } + }); + + if ( stack.length > 0 ) { + const swapItems = function(items, indexBefore, indexAfter) { + items[indexAfter] = items.splice(indexBefore, 1, items[indexAfter])[0]; + }; + + if ( isEdit && !isDisconnected ) { + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader && canCopy ) { + itemsIcon.push({ + event: 'cut', + icon: 'icon-cut' + }); + + // Swap 'Copy' and 'Cut' + swapItems(itemsIcon, 0, 1); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsIcon.push({ + event: 'paste', + icon: 'icon-paste' + }); + } + + if ( isTable && api.CheckBeforeMergeCells() && !lockedTable && !lockedHeader) { + itemsText.push({ + caption: _t.menuMerge, + event: 'merge' + }); + } + + if ( isTable && api.CheckBeforeSplitCells() && !lockedTable && !lockedHeader ) { + itemsText.push({ + caption: _t.menuSplit, + event: 'split' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsText.push({ + caption: _t.menuDelete, + event: 'delete' + }); + } + + if ( isTable && !lockedTable && !lockedText && !lockedHeader ) { + itemsText.push({ + caption: _t.menuDeleteTable, + event: 'deletetable' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ){ + itemsText.push({ + caption: _t.menuEdit, + event: 'edit' + }); + } + + if ( !!api.can_AddHyperlink() && !lockedHeader) { + itemsText.push({ + caption: _t.menuAddLink, + event: 'addlink' + }); + } + + if(isLink && !lockedHeader) { + itemsText.push({ + caption: _t.menuEditLink, + event: 'editlink' + }); + } + + if ( canReview ) { + if (contextMenu.inRevisionChange) { + itemsText.push({ + caption: _t.menuReviewChange, + event: 'reviewchange' + }); + } else { + itemsText.push({ + caption: _t.menuReview, + event: 'review' + }); + } + } + + if ( contextMenu.isComments && canViewComments ) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + const isObject = isShape || isChart || isImage || isTable; + const hideAddComment = !canViewComments || api.can_AddQuotedComment() === false || lockedText || lockedTable || lockedImage || lockedHeader || (!isText && isObject); + if ( !hideAddComment ) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + } + + if ( isLink ) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + } + + if ( Device.phone && itemsText.length > 2 ) { + contextMenu.extraItems = itemsText.splice(2,itemsText.length, { + caption: _t.menuMore, + event: 'showActionSheet' + }); + } + + const menuItems = itemsIcon.concat(itemsText); + // console.log('EditorUIController.ContextMenu.mapMenuItems', menuItems); + return menuItems; + } , + handleMenuItemClick: (contextMenu, action) => { + // console.log('EditorUIController.ContextMenu.handleMenuItemClick', contextMenu, action); + + const api = Common.EditorApi.get(); + switch (action) { + case 'edit': + setTimeout(() => { + contextMenu.props.openOptions('edit'); + }, 0); + return true; + case 'addcomment': + Common.Notifications.trigger('addcomment'); + return true; + case 'merge': + api.MergeCells(); + return true; + case 'delete': + api.asc_Remove(); + return true; + case 'deletetable': + api.remTable(); + return true; + case 'editlink': + setTimeout(() => { + contextMenu.props.openOptions('edit', 'link'); + }, 400) + return true; + } + + return false; + }, + // This method handles the menu items shown when the user is in edit mode but + // can't edit the form i.e: in viewer mode or when the document is protected + // This is mostly a copy of the else statement of ContextMenu.initMenuItems + // with the bare minimum modifications so it can execute. + // It is done that way on purpose even if some code ends up duplicated + // This should make noticing and following Onlyoffice changes easier + mapMenuItemsReadOnly: (contextMenu) => { + const { t } = contextMenu.props; + const _t = t("ContextMenu", { returnObjects: true }); + + const { isEdit, isDisconnected, isVersionHistoryMode } = contextMenu.props; + const { isComments, canViewComments, canCoAuthoring, canComments } = contextMenu.props; + + const api = Common.EditorApi.get(); + const stack = api.getSelectedElements(); + const canCopy = api.can_CopyCut(); + + let itemsIcon = [], + itemsText = []; + + let isText = false, + isTable = false, + isImage = false, + isChart = false, + isShape = false, + isLink = false, + isSlide = false, + isObject, + locked = false; + + stack.forEach(item => { + const objectType = item.get_ObjectType(), + objectValue = item.get_ObjectValue(); + locked = typeof objectValue.get_Locked === 'function' ? objectValue.get_Locked() : false; + + if (objectType == Asc.c_oAscTypeSelectElement.Paragraph) { + isText = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Image) { + isImage = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Chart) { + isChart = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Shape) { + isShape = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Table) { + isTable = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Hyperlink) { + isLink = true; + } else if (objectType == Asc.c_oAscTypeSelectElement.Slide) { + isSlide = true; + } + }); + + isObject = isText || isImage || isChart || isShape || isTable; + + if (canCopy && isObject) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + } + if(!isDisconnected && !isVersionHistoryMode) { + if (canViewComments && isComments && !isEdit) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + if (!isChart && api.can_AddQuotedComment() !== false && canCoAuthoring && canComments && !locked) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + + if (isLink) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + } + + return itemsIcon.concat(itemsText); + } +}; + +// This objects holds the mysterious functions in focusObjects.js inside the intf object +// These for now are based on the latest valid implementation from the mobile editor +// Code taken from +// https://github.com/ONLYOFFICE/web-apps/commit/fe1a9e16d3c18cb5e9c2cc1aa512649c348aa283#diff-1a8252788e3c9baa4e6bcdd6a91d4718fcf3ac90f0f12b4e3263f9cccba53f8d +EditorUIController.Intf = function (storeFocusObjects) { + var obj = {}; + + obj.filterFocusObjects = () => { + const _settings = []; + let no_text = true; + for (let object of storeFocusObjects._focusObjects) { + const type = object.get_ObjectType(), + objectValue = object.get_ObjectValue(); + if (Asc.c_oAscTypeSelectElement.Paragraph == type) { + if ( !objectValue.get_Locked() ) + no_text = false; + } else if (Asc.c_oAscTypeSelectElement.Table == type) { + if ( !objectValue.get_Locked() ) { + _settings.push('table'); + no_text = false; + } + } else if (Asc.c_oAscTypeSelectElement.Slide == type) { + if ( !(objectValue.get_LockLayout() || objectValue.get_LockBackground() || objectValue.get_LockTransition() || objectValue.get_LockTiming() )) + _settings.push('slide'); + } else if (Asc.c_oAscTypeSelectElement.Image == type) { + if ( !objectValue.get_Locked() ) + _settings.push('image'); + } else if (Asc.c_oAscTypeSelectElement.Chart == type) { + if ( !objectValue.get_Locked() ) + _settings.push('chart'); + } else if (Asc.c_oAscTypeSelectElement.Shape == type && !objectValue.get_FromChart()) { + if ( !objectValue.get_Locked() ) { + _settings.push('shape'); + no_text = false; + } + } else if (Asc.c_oAscTypeSelectElement.Hyperlink == type) { + _settings.push('hyperlink'); + } + } + if (!no_text && _settings.indexOf('image') < 0) + _settings.unshift('text'); + const resultArr = _settings.filter((value, index, self) => self.indexOf(value) === index); //get uniq array + // Exclude hyperlink if text is locked + if (resultArr.indexOf('hyperlink') > -1 && resultArr.indexOf('text') < 0) { + resultArr.splice(resultArr.indexOf('hyperlink'), 1); + } + // Exclude shapes if chart exist + if (resultArr.indexOf('chart') > -1 && resultArr.indexOf('shape') > -1) { + resultArr.splice(resultArr.indexOf('shape'), 1); + } + return resultArr; + } + + obj.getSlideObject = () => { + const slides = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Slide) { + slides.push(object); + } + } + if (slides.length > 0) { + const object = slides[slides.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getParagraphObject = () => { + const paragraphs = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Paragraph) { + paragraphs.push(object); + } + } + if (paragraphs.length > 0) { + const object = paragraphs[paragraphs.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getShapeObject = () => { + const shapes = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Shape) { + shapes.push(object); + } + } + if (shapes.length > 0) { + const object = shapes[shapes.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getImageObject = () => { + const images = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Image && object.get_ObjectValue()) { + images.push(object); + } + } + if (images.length > 0) { + const object = images[images.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getTableObject = () => { + const tables = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Table) { + tables.push(object); + } + } + if (tables.length > 0) { + const object = tables[tables.length - 1]; // get top table + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getChartObject = () => { + const charts = []; + + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Chart) { + charts.push(object); + } + } + + if (charts.length > 0) { + const object = charts[charts.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getLinkObject = () => { + const links = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink) { + links.push(object); + } + } + if (links.length > 0) { + const object = links[links.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + return obj; +} + +export default EditorUIController; diff --git a/patches/web-apps-mobile/word/patch.jsx b/patches/web-apps-mobile/word/patch.jsx new file mode 100644 index 0000000..bb4e69a --- /dev/null +++ b/patches/web-apps-mobile/word/patch.jsx @@ -0,0 +1,640 @@ +import React, {Fragment} from 'react'; +import {Link} from 'framework7-react'; +import { Device } from '../../web-apps/apps/common/mobile/utils/device'; +import { + AddCommentController, + EditCommentController, +} from "../../web-apps/apps/common/mobile/lib/controller/collaboration/Comments"; + +const EditorUIController = () => { + return null +}; + +EditorUIController.isSupportEditFeature = () => { + return true; +}; + +/** + +This is the file where the magic happens. +Try to figure out a working version from a both the old commits +and my hunches from hacking around in the old webapp editor + +https://github.com/ONLYOFFICE/web-apps/commit/45a9058da461a9da67ccb389faad08bce8cbda55 +*/ + +EditorUIController.getToolbarOptions = (options) => { + // console.log('EditorUIController.getToolbarOptions' ,options); + return ( + + options.onEditClick()}> + options.onAddClick()}> + + ); +}; + +EditorUIController.getUndoRedo = (options) => { + // console.log('EditorUIController.getUndoRedo', options); + return ( + + options.onUndoClick()}> + options.onRedoClick()}> + + ); +} + +EditorUIController.getEditCommentControllers = () => { + // console.log('EditorUIController.getEditCommentControllers'); + return ( + + + + + ); +} + +// https://github.com/ONLYOFFICE/web-apps/commit/ee15cf025dd64fe0f48eba015c7bdcea0446a38b +EditorUIController.initFonts = (storeTextSettings) => { + // console.log('EditorUIController.initFonts', storeTextSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorFonts', (fonts, select) => { + // console.log('asc_onInitEditorFonts', fonts, select); + storeTextSettings.initEditorFonts(fonts, select); + }); + api.asc_registerCallback('asc_onFontFamily', (font) => { + // console.log('asc_onFontFamily', font); + storeTextSettings.resetFontName(font); + }); + api.asc_registerCallback('asc_onFontSize', (size) => { + // console.log('asc_onFontSize', size); + storeTextSettings.resetFontSize(size); + }); + api.asc_registerCallback('asc_onBold', (isBold) => { + // console.log('asc_onBold', isBold); + storeTextSettings.resetIsBold(isBold); + }); + api.asc_registerCallback('asc_onItalic', (isItalic) => { + // console.log('asc_onItalic', isItalic); + storeTextSettings.resetIsItalic(isItalic); + }); + api.asc_registerCallback('asc_onUnderline', (isUnderline) => { + // console.log('asc_onUnderline', isUnderline); + storeTextSettings.resetIsUnderline(isUnderline); + }); + api.asc_registerCallback('asc_onStrikeout', (isStrikeout) => { + // console.log('asc_onStrikeout', isStrikeout); + storeTextSettings.resetIsStrikeout(isStrikeout); + }); +}; +EditorUIController.initEditorStyles = (storeParagraphSettings) => { + // console.log('EditorUIController.initEditorStyles', storeParagraphSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitEditorStyles', (styles) => { + // console.log('asc_onInitEditorStyles', styles); + storeParagraphSettings.initEditorStyles(styles); + }); + api.asc_registerCallback('asc_onParaStyleName', (name) => { + // console.log('asc_onParaStyleName', name); + storeParagraphSettings.changeParaStyleName(name); + }); +} +EditorUIController.initFocusObjects = (storeFocusObjects) => { + storeFocusObjects.intf = EditorUIController.Intf(storeFocusObjects); + // console.log('EditorUIController.initFocusObjects', storeFocusObjects); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onFocusObject', objects => { + // console.log('asc_onFocusObject', objects); + storeFocusObjects.resetFocusObjects(objects); + }); +} +EditorUIController.initTableTemplates = (storeTableSettings) => { + // console.log('EditorUIController.initTableTemplates', storeTableSettings); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onInitTableTemplates', (templates) => { + // console.log('asc_onInitTableTemplates', templates); + storeTableSettings.initTableTemplates(templates); + }); +} + +EditorUIController.initThemeColors = () => { + // console.log('EditorUIController.initThemeColors'); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onSendThemeColors', (colors, standart_colors) => { + // console.log('asc_onSendThemeColors', colors, standart_colors); + Common.Utils.ThemeColor.setColors(colors, standart_colors); + }); +} + +EditorUIController.updateChartStyles = (storeChartSettings, storeFocusObjects) => { + // console.log('EditorUIController.updateChartStyles', storeChartSettings, storeFocusObjects); + const api = Common.EditorApi.get(); + api.asc_registerCallback('asc_onUpdateChartStyles', () => { + // console.log('asc_onUpdateChartStyles', storeFocusObjects.chartObject); + if (storeFocusObjects.chartObject && storeFocusObjects.chartObject.get_ChartProperties()) { + storeChartSettings.updateChartStyles(api.asc_getChartPreviews(storeFocusObjects.chartObject.get_ChartProperties().getType())); + } + }); +} + +EditorUIController.ContextMenu = { + // Both those methods are taken from this commit: + // https://github.com/ONLYOFFICE/web-apps/commit/b0c9a1c5d28371d78ae54854f74f9bdaa8ee9efa + // 'edit' in handleMenuItemClick is from + // https://github.com/ONLYOFFICE/web-apps/commit/cb62ade641dbdc2c7a2779d408b6d82bbf738500 + mapMenuItems: (contextMenu) => { + // console.log('EditorUIController.ContextMenu.mapMenuItems', contextMenu); + + if ( !Common.EditorApi ) return []; + + const { t } = contextMenu.props; + const _t = t("ContextMenu", { returnObjects: true }); + + const { isEdit, canViewComments, canReview, isDisconnected } = contextMenu.props; + const { isViewer, isProtected } = contextMenu.props; + + // This probably needs to be refired + if(!isEdit || isViewer || isProtected) + return EditorUIController.ContextMenu.mapMenuItemsReadOnly(contextMenu); + + const api = Common.EditorApi.get(); + const stack = api.getSelectedElements(); + const canCopy = api.can_CopyCut(); + + let itemsIcon = [], + itemsText = []; + + if ( canCopy ) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + } + + if ( canViewComments && contextMenu.isComments && !isEdit ) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + let isText = false, + isTable = false, + isImage = false, + isChart = false, + isShape = false, + isLink = false, + lockedText = false, + lockedTable = false, + lockedImage = false, + lockedHeader = false; + + stack.forEach(item => { + const objectType = item.get_ObjectType(), + objectValue = item.get_ObjectValue(); + + if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { + lockedHeader = objectValue.get_Locked(); + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { + lockedText = objectValue.get_Locked(); + isText = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Image ) { + lockedImage = objectValue.get_Locked(); + if ( objectValue && objectValue.get_ChartProperties() ) { + isChart = true; + } else + if ( objectValue && objectValue.get_ShapeProperties() ) { + isShape = true; + } else { + isImage = true; + } + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Table ) { + lockedTable = objectValue.get_Locked(); + isTable = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { + isLink = true; + } + }); + + if ( stack.length > 0 ) { + const swapItems = function(items, indexBefore, indexAfter) { + items[indexAfter] = items.splice(indexBefore, 1, items[indexAfter])[0]; + }; + + if ( isEdit && !isDisconnected ) { + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader && canCopy ) { + itemsIcon.push({ + event: 'cut', + icon: 'icon-cut' + }); + + // Swap 'Copy' and 'Cut' + swapItems(itemsIcon, 0, 1); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsIcon.push({ + event: 'paste', + icon: 'icon-paste' + }); + } + + if ( isTable && api.CheckBeforeMergeCells() && !lockedTable && !lockedHeader) { + itemsText.push({ + caption: _t.menuMerge, + event: 'merge' + }); + } + + if ( isTable && api.CheckBeforeSplitCells() && !lockedTable && !lockedHeader ) { + itemsText.push({ + caption: _t.menuSplit, + event: 'split' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ) { + itemsText.push({ + caption: _t.menuDelete, + event: 'delete' + }); + } + + if ( isTable && !lockedTable && !lockedText && !lockedHeader ) { + itemsText.push({ + caption: _t.menuDeleteTable, + event: 'deletetable' + }); + } + + if ( !lockedText && !lockedTable && !lockedImage && !lockedHeader ){ + itemsText.push({ + caption: _t.menuEdit, + event: 'edit' + }); + } + + if ( !!api.can_AddHyperlink() && !lockedHeader) { + itemsText.push({ + caption: _t.menuAddLink, + event: 'addlink' + }); + } + + if ( canReview ) { + if (contextMenu.inRevisionChange) { + itemsText.push({ + caption: _t.menuReviewChange, + event: 'reviewchange' + }); + } else { + itemsText.push({ + caption: _t.menuReview, + event: 'review' + }); + } + } + + if ( contextMenu.isComments && canViewComments ) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + const isObject = isShape || isChart || isImage || isTable; + const hideAddComment = !canViewComments || api.can_AddQuotedComment() === false || lockedText || lockedTable || lockedImage || lockedHeader || (!isText && isObject); + if ( !hideAddComment ) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + } + + if ( isLink ) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + itemsText.push({ + caption: _t.menuEditLink, + event: 'editlink' + }); + } + + if ( Device.phone && itemsText.length > 2 ) { + contextMenu.extraItems = itemsText.splice(2,itemsText.length, { + caption: _t.menuMore, + event: 'showActionSheet' + }); + } + + const menuItems = itemsIcon.concat(itemsText); + // console.log('EditorUIController.ContextMenu.mapMenuItems', menuItems); + return menuItems; + } , + handleMenuItemClick: (contextMenu, action) => { + // console.log('EditorUIController.ContextMenu.handleMenuItemClick', contextMenu, action); + + const api = Common.EditorApi.get(); + switch (action) { + case 'edit': + setTimeout(() => { + contextMenu.props.openOptions('edit'); + }, 400); + return true; + case 'addcomment': + Common.Notifications.trigger('addcomment'); + return true; + case 'merge': + api.MergeCells(); + return true; + case 'delete': + api.asc_Remove(); + return true; + case 'deletetable': + api.remTable(); + return true; + case 'editlink': + setTimeout(() => { + contextMenu.props.openOptions('edit', 'link'); + }, 400) + return true; + } + + return false; + }, + // This method handles the menu items shown when the user is in edit mode but + // can't edit the form i.e: in viewer mode or when the document is protected + // This is mostly a copy of the else statement of ContextMenu.initMenuItems + // with the bare minimum modifications so it can execute. + // It is done that way on purpose even if some code ends up duplicated + // This should make noticing and following Onlyoffice changes easier + mapMenuItemsReadOnly: (contextMenu) => { + const { t } = contextMenu.props; + const _t = t("ContextMenu", {returnObjects: true}); + + const { isEdit, canFillForms, isDisconnected, isViewer, canEditComments, isProtected, typeProtection } = contextMenu.props; + const { isComments, canViewComments, canCoAuthoring, canComments, dataDoc } = contextMenu.props; + + const api = Common.EditorApi.get(); + const inToc = api.asc_GetTableOfContentsPr(true); + const stack = api.getSelectedElements(); + const canCopy = api.can_CopyCut(); + const docExt = dataDoc ? dataDoc.fileType : ''; + const isAllowedEditing = !isProtected || typeProtection === Asc.c_oAscEDocProtect.TrackedChanges; + const isAllowedCommenting = typeProtection === Asc.c_oAscEDocProtect.Comments; + + let isText = false, + isObject = false, + isLink = false, + locked = false; + + stack.forEach(item => { + const objectType = item.get_ObjectType(), + objectValue = item.get_ObjectValue(); + if ( objectType == Asc.c_oAscTypeSelectElement.Header ) { + locked = objectValue.get_Locked(); + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Paragraph ) { + locked = objectValue.get_Locked(); + isText = true; + } else + if ( objectType == Asc.c_oAscTypeSelectElement.Image || objectType == Asc.c_oAscTypeSelectElement.Table) { + locked = objectValue.get_Locked(); + isObject = true; + } else if ( objectType == Asc.c_oAscTypeSelectElement.Hyperlink ) { + isLink = true; + } + }); + + let itemsIcon = [], + itemsText = []; + + if (canCopy) { + itemsIcon.push({ + event: 'copy', + icon: 'icon-copy' + }); + } + + if (!isDisconnected) { + if (canFillForms && canCopy && !locked && (!isViewer || docExt === 'oform') && isAllowedEditing) { + itemsIcon.push({ + event: 'cut', + icon: 'icon-cut' + }); + } + + if (canFillForms && canCopy && !locked && (!isViewer || docExt === 'oform') && isAllowedEditing) { + itemsIcon.push({ + event: 'paste', + icon: 'icon-paste' + }); + } + + if (canViewComments && isComments) { + itemsText.push({ + caption: _t.menuViewComment, + event: 'viewcomment' + }); + } + + if (api.can_AddQuotedComment() !== false && canCoAuthoring && canComments && !locked && !(!isText && isObject) && (!isViewer || canEditComments) && (isAllowedEditing || isAllowedCommenting)) { + itemsText.push({ + caption: _t.menuAddComment, + event: 'addcomment' + }); + } + } + + if (isLink) { + itemsText.push({ + caption: _t.menuOpenLink, + event: 'openlink' + }); + + if(isAllowedEditing && !isViewer) { + itemsText.push({ + caption: t('ContextMenu.menuEditLink'), + event: 'editlink' + }); + } + } + + if(inToc && isEdit && !isViewer && isAllowedEditing) { + itemsText.push({ + caption: t('ContextMenu.textRefreshEntireTable'), + event: 'refreshEntireTable' + }); + itemsText.push({ + caption: t('ContextMenu.textRefreshPageNumbersOnly'), + event: 'refreshPageNumbers' + }); + } + + return itemsIcon.concat(itemsText); + } +}; + +// This objects holds the mysterious functions in focusObjects.js inside the intf object +// These for now are based on the latest valid implementation from the mobile editor +// The easiest way to fix it was to restores those in focusObjects.js: A way to improve would be to do that here. +// https://github.com/ONLYOFFICE/web-apps/commit/b0c9a1c5d28371d78ae54854f74f9bdaa8ee9efa +// https://github.com/ONLYOFFICE/web-apps/blame/bc7713368a787e01a0ce93a2d7956c77c762e1c5/apps/documenteditor/mobile/src/store/focusObjects.js (for settings or filterFocusObjects) + +EditorUIController.Intf = function (storeFocusObjects) { + var obj = {}; + + // console.log(window.Asc.c_oAscTypeSelectElement); + + obj.filterFocusObjects = () => { + const _settings = []; + for (let object of storeFocusObjects._focusObjects) { + let type = object.get_ObjectType(); + if (Asc.c_oAscTypeSelectElement.Paragraph === type) { + _settings.push('text', 'paragraph'); + } else if (Asc.c_oAscTypeSelectElement.Table === type) { + _settings.push('table'); + } else if (Asc.c_oAscTypeSelectElement.Image === type) { + if (object.get_ObjectValue().get_ChartProperties()) { + // Exclude shapes if chart exist + let si = _settings.indexOf('shape'); + si < 0 ? _settings.push('chart') : _settings.splice(si,1,'chart'); + } else if (object.get_ObjectValue().get_ShapeProperties() && !_settings.includes('chart')) { + _settings.push('shape'); + } else { + _settings.push('image'); + } + } else if (Asc.c_oAscTypeSelectElement.Hyperlink === type) { + _settings.push('hyperlink'); + } else if (Asc.c_oAscTypeSelectElement.Header === type) { + _settings.push('header'); + } + } + return _settings.filter((value, index, self) => self.indexOf(value) === index); + } + + obj.getHeaderObject = () => { + const headers = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Header) { + headers.push(object); + } + } + if (headers.length > 0) { + const object = headers[headers.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getParagraphObject = () => { + const paragraphs = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Paragraph) { + paragraphs.push(object); + } + } + if (paragraphs.length > 0) { + const object = paragraphs[paragraphs.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getShapeObject = () => { + const shapes = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() === Asc.c_oAscTypeSelectElement.Image) { + if (object.get_ObjectValue() + && typeof object.get_ObjectValue().get_ShapeProperties === "function" + && object.get_ObjectValue().get_ShapeProperties()) { + shapes.push(object); + } + } + } + if (shapes.length > 0) { + const object = shapes[shapes.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getImageObject = () => { + const images = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Image) { + const imageObject = object.get_ObjectValue(); + if (imageObject && imageObject.get_ShapeProperties() === null && imageObject.get_ChartProperties() === null) { + images.push(object); + } + } + } + if (images.length > 0) { + const object = images[images.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getTableObject = () => { + const tables = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Table) { + tables.push(object); + } + } + if (tables.length > 0) { + const object = tables[tables.length - 1]; // get top table + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getChartObject = () => { + const charts = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectValue() + && typeof object.get_ObjectValue().get_ChartProperties === 'function' + && object.get_ObjectValue().get_ChartProperties() ) { + charts.push(object); + } + } + if (charts.length > 0) { + const object = charts[charts.length - 1]; // get top table + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + obj.getLinkObject = () => { + const links = []; + for (let object of storeFocusObjects._focusObjects) { + if (object.get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink) { + links.push(object); + } + } + if (links.length > 0) { + const object = links[links.length - 1]; // get top + return object.get_ObjectValue(); + } else { + return undefined; + } + } + + return obj; +} + +export default EditorUIController; diff --git a/web-apps.patch b/patches/web-apps/0001-add-mobile.patch similarity index 76% rename from web-apps.patch rename to patches/web-apps/0001-add-mobile.patch index e3272b0..4bd35c9 100644 --- a/web-apps.patch +++ b/patches/web-apps/0001-add-mobile.patch @@ -1,3 +1,14 @@ +From de497cfe4ac6a6cdb522bb7463624c91d282b721 Mon Sep 17 00:00:00 2001 +From: Fabrice +Date: Mon, 14 Oct 2024 16:57:25 +0200 +Subject: [PATCH] add mobile + +--- + apps/documenteditor/mobile/src/lib/patch.jsx | 2 +- + apps/presentationeditor/mobile/src/lib/patch.jsx | 2 +- + apps/spreadsheeteditor/mobile/src/lib/patch.jsx | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + diff --git a/apps/documenteditor/mobile/src/lib/patch.jsx b/apps/documenteditor/mobile/src/lib/patch.jsx index 963aca451c..1ee48e8568 100644 --- a/apps/documenteditor/mobile/src/lib/patch.jsx @@ -35,3 +46,6 @@ index ec7b37a2ce..bfd8795839 100644 +EditorUIController.isSupportEditFeature = () => true; export default EditorUIController; +-- +2.30.2 + diff --git a/AGPLv3_Logo.svg.png b/welcome/img/AGPLv3_Logo.svg.png similarity index 100% rename from AGPLv3_Logo.svg.png rename to welcome/img/AGPLv3_Logo.svg.png diff --git a/favicon.ico b/welcome/img/favicon.ico similarity index 100% rename from favicon.ico rename to welcome/img/favicon.ico diff --git a/ws-logo.png b/welcome/img/ws-logo.png similarity index 100% rename from ws-logo.png rename to welcome/img/ws-logo.png diff --git a/welcome.html b/welcome/index.html similarity index 100% rename from welcome.html rename to welcome/index.html