libreoffice-online/ios/Mobile/Document.mm

149 lines
6.4 KiB
Text
Raw Normal View History

// -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*-
//
// This file is part of the LibreOffice project.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#import "config.h"
#import <algorithm>
// This is not "external" code in the UNO-based extensions sense. To be able to include
// <comphelper/lok.hxx>, we must #define LIBO_INTERNAL_ONLY.
#define LIBO_INTERNAL_ONLY
#include <sal/config.h>
#include <sal/log.hxx>
#include <rtl/ustring.hxx>
#include <comphelper/lok.hxx>
#include <i18nlangtag/languagetag.hxx>
#import "ios.h"
#import "AppDelegate.h"
#import "Document.h"
#import "DocumentViewController.h"
#import "ClientSession.hpp"
#import "DocumentBroker.hpp"
#import "FakeSocket.hpp"
#import "Kit.hpp"
#import "KitHelper.hpp"
#import "Log.hpp"
#import "LOOLWSD.hpp"
#import "Protocol.hpp"
@implementation Document
- (id)contentsForType:(NSString*)typeName error:(NSError **)errorPtr {
// Somehow this doesn't feel right, creating an NSFileWrapper around the file that was given to
// us for loadFromContents. I get the vague feeling that the file is perhaps just a temporary
// data container created by the system for us to be used while loading the document data, and
// not the actual permanent document, especially in the case of things like NextCloud. Or is it?
// Is savng back to it (which we have already done by the time we get here, in the LO core code)
// correct? This does seem to work, though. Anyway, clearly I need to read more documentation
// for UIDocument etc.
return [[NSFileWrapper alloc] initWithURL:[self fileURL] options:0 error:errorPtr];
}
- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError **)errorPtr {
Add a way to have templates specific to a customer deployment Add two settings: One setting "Template list URL" is a string that should either be empty (the typical case for a random user of the app), or contain a https: URL. If this setting is empty, only the templates bundled in the app are provided. If the "Template list URL" is non-empty, it should be a https: URL pointing to a text file (or dynamically generated text resource). That file is downloaded and read when the app starts. Each line in the file should either be a comment (starting with a hash '#'), or a https: URL pointing to a template document, that is of type .ott, .ots, or .otp. That document is downloaded if it hasn't been downloaded already, or if its time stamp is newer than that of the already downloaded copy. Also a thumbnail image for the template, formed by appending ".png" to its URL, is downloaded, if available. Any previously downloaded templates that aren't mentioned in the list file are removed. The intent is that in some managed mass deployment environment, the mobile device management software would set up this setting, so that the end-user devices would see the same templates. Obviously, this URL does not have to point to a static file on a web server, but could point to some dynamically generated resource on a web server, that enumerates the templates available on the server and returns their URLs as a text document. Another setting is "Empty tile cache next time". This is a toggle. If toggled on, the next time a document is opened in the app, the tile cache is emptied (and the toggle is reset off). This is mostly for potential problem solving, and might be removd later. Various refactoring to support the new functionality. Change-Id: Ie2ebf032acb9e43bb1c6f7ae4d0c449ae66eaa05
2018-12-17 14:18:49 -06:00
// If this method is called a second time on the same Document object, just ignore it. This
// seems to happen occastionally when the device is awakened after sleep. See tdf#122543.
if (fakeClientFd >= 0)
return YES;
fakeClientFd = fakeSocketSocket();
NSString *uri = [[self fileURL] absoluteString];
comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(OUString::fromUtf8(OString([app_locale UTF8String])), true));
NSURL *url = [[NSBundle mainBundle] URLForResource:@"loleaflet" withExtension:@"html"];
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
components.queryItems = @[ [NSURLQueryItem queryItemWithName:@"file_path" value:uri],
[NSURLQueryItem queryItemWithName:@"closebutton" value:@"1"],
[NSURLQueryItem queryItemWithName:@"permission" value:@"edit"],
[NSURLQueryItem queryItemWithName:@"lang" value:app_locale]
];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:components.URL];
[self.viewController.webView loadRequest:request];
return YES;
}
- (void)send2JS:(const char *)buffer length:(int)length {
LOG_TRC("To JS: " << LOOLProtocol::getAbbreviatedMessage(buffer, length).c_str());
NSString *js;
// Check if the message is binary. We say that any message that isn't just a single line is
// "binary" even if that strictly speaking isn't the case; for instance the commandvalues:
// message has a long bunch of non-binary JSON on multiple lines. But _onMessage() in Socket.js
// handles it fine even if such a message, too, comes in as an ArrayBuffer. (Look for the
// "textMsg = String.fromCharCode.apply(null, imgBytes);".)
const char *newline = (const char *)memchr(buffer, '\n', length);
if (newline != nullptr) {
// The data needs to be an ArrayBuffer
js = @"window.TheFakeWebSocket.onmessage({'data': Base64ToArrayBuffer('";
js = [js stringByAppendingString: [[NSData dataWithBytes:buffer length:length] base64EncodedStringWithOptions:0]];
js = [js stringByAppendingString:@"')});"];
NSString *subjs = [js substringToIndex:std::min(100ul, js.length)];
if (subjs.length < js.length)
subjs = [subjs stringByAppendingString:@"..."];
// LOG_TRC("Evaluating JavaScript: " << [subjs UTF8String]);
dispatch_async(dispatch_get_main_queue(), ^{
[self.viewController.webView evaluateJavaScript:js
completionHandler:^(id _Nullable obj, NSError * _Nullable error)
{
if (error) {
LOG_ERR("Error after " << [subjs UTF8String] << ": " << [error.localizedDescription UTF8String]);
}
}
];
});
} else {
const unsigned char *ubufp = (const unsigned char *)buffer;
std::vector<char> data;
for (int i = 0; i < length; i++) {
if (ubufp[i] < ' ' || ubufp[i] == '\'' || ubufp[i] == '\\') {
data.push_back('\\');
data.push_back('x');
data.push_back("0123456789abcdef"[(ubufp[i] >> 4) & 0x0F]);
data.push_back("0123456789abcdef"[ubufp[i] & 0x0F]);
} else {
data.push_back(ubufp[i]);
}
}
data.push_back(0);
js = @"window.TheFakeWebSocket.onmessage({'data': '";
js = [js stringByAppendingString:[NSString stringWithUTF8String:data.data()]];
js = [js stringByAppendingString:@"'});"];
// LOG_TRC("Evaluating JavaScript: " << [js UTF8String]);
dispatch_async(dispatch_get_main_queue(), ^{
[self.viewController.webView evaluateJavaScript:js
completionHandler:^(id _Nullable obj, NSError * _Nullable error)
{
if (error) {
LOG_ERR("Error after " << [js UTF8String] << ": " << [[error localizedDescription] UTF8String]);
NSString *jsException = error.userInfo[@"WKJavaScriptExceptionMessage"];
if (jsException != nil)
LOG_ERR("JavaScript exception: " << [jsException UTF8String]);
}
}
];
});
}
}
@end
// vim:set shiftwidth=4 softtabstop=4 expandtab: