
And do not write us any service under Opera United? Here I will talk about the features of programming a novelty from the Opera on the example of my service "
Stream media ". Official documentation is still incomplete and contains many errors, and in this article we will try to get around them.

Further, we will delve into the technology gradually. It is advisable that you have already read the
manual for creating the first application , since some obvious facts are omitted here.
config.xml

The main file of your service. After installation it is cached and its changes do not lead to changes in real time. You can only remove the service and reinstall it. This is different from the server-side javascript behavior (it helps on-off service) and even more so from client-side javascript and html (they change in real time).
<widget network="public private">
<widgetname>Stream media</widgetname>
<description>Stream media online from your own computer with Opera Unite! Audio, video and etc.</description>
<author>
<name>avenu</name>
<organisation></organisation>
</author>
<feature name="http://xmlns.opera.com/webserver">
<param name="type" value="service"/>
<param name="servicepath" value="stream"/>
</feature>
<feature name="http://xmlns.opera.com/fileio">
<param name="folderhint" value="home" />
</feature>
</widget>
Pay attention to the network = "public private" attribute of the widget tag - I recommend to enable it if you work with external sites. Consider, because of the peculiarities of AJAX, you cannot connect to them in a simple way. But with the help of this attribute the widget itself will be able to connect to the Internet using the same AJAX on the server.
In addition, the File I / O API is connected here:
<feature name="http://xmlns.opera.com/fileio">
<param name="folderhint" value="home" />
</feature>
and how the address of the service will look like:
<param name="servicepath" value="stream"/>
Javascript

To begin with, we’ll enter into our brains the information that Opera Unite service is a special kind of widget (yes, those very widgets from Opera) with a number of features. From this it follows that the widget API functions are applicable to it.
Javascript is divided into server and client. On the server, the entire logic of Opera United (regards, an analogue of PHP / Ruby / Python / etc.) Is prescribed; on the client, the logic of the site that the user sees is implemented. For example, you can use jQuery. HTML in Unite resembles a view from MVC (respectively, server-side javascript is an analogue of a controller). UNITE generates HTML depending on the variables that the script passed to it.
Accordingly, client scripts, CSS, files are put in the default folder public_html. The server-side script and HTML can be put in any folders, you specify the path to them yourself. To do this, in the root, next to the future configuration file, the index.html file is created, it indicates the construction of the type:
<!DOCTYPE html>
<script src="script/markuper.js"></script>
<script src="script/functions.js"></script>
<script src="script/operafunctions.js"></script>
<script src="script/script.js"></script>
HTML will be connected later with the help of markup mechanisms.
Next, in one of the javascript created and connected, for example script.js from the script folder, we enable the handling of the "server turned on" event, and add request processing (URL) to the scripts:
window.onload = function ()
webserver = opera.io.webserver
if (webserver)
{
//Handle requests for various URLs
webserver.addEventListener('_index', showEntryList, false);
webserver.addEventListener('shared', download, false);
}
}
_index is the identifier of the initial page of the service. This name is the default. In the above code, the showEntryList function will be called to access the site root. Similarly, I added a call to the download function to access an address of the / service-name / shared / bla / bla / bla / bla / bla type in order to resolve some features of sharing files in Unite.
')
A variable of type Event will be available in the functions, from which you can get various useful objects:
var response = e.connection.response;
var request = e.connection.request;
However, you can get the Request or Respone object using the global opera object, but at the time of accessing it, the Request may already be new:
opera.io.webserver.connections.request
opera.io.webserver.connections.response
Using Request, we can pull data from a GET / POST (respectively) request:
var index = request.queryItems['id'][0];
var password = request.bodyItems.passwords[0];
Reponse will display a user page:
response.write( '<!DOCTYPE html>'
+ '<html><head><title>Entries</title></head>'
+ '<body><ul>'
);
However, this is not the most convenient way to work with HTML. Consider the Markuper mechanism.
HTML

Markuper allows you to create HTML files with server logic. This is something like a template engine a la Tidy. To start working with it, we need in index.html in the root of the service folder to add a link to the markuper.js file, which will be located next to your server scripts. Download it here:
dev.opera.com/libraries/markuper/markuper.js . Please note - the mark caper _ needs to be saved in the service folder and connect _! .. It is not available by default.
Now you can create a template file, for example, index-template.html and add it to some folder in the root (or deeper), for example, templates. Then in the script a new Markapper object will look like this:
var template = new Markuper( 'templates/index-template.html');
The file itself will look something like this:
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Tutorial</title>
</head>
<body>
<h1>{{name}} Tutorial</h1>
<p>
This variable is further down the data object hierarchy:
'{{further.down.the.hierarchy}}'
</p>
</body>
</html>
{{path.to.variable}} is the path in the variable by its properties. We will transfer it from the script like this:
function handleRequest( event )
{
var response = event.connection.response;
var data =
{
name : 'Template',
further : {down: {the : {hierarchy: 'yes it is!' } } }
};
var template = new Markuper( 'templates/tutorial.html', data );
response.write( template.parse().html() );
response.close();
}
Pay attention to the need to parse the Markaper: template.parse () and convert the object to HTML: template.parse (). Html ().
File sharing

The most important function, about which very little is said in the API and there is nothing at all in the examples. So, having specified the following construction in config.xml, we force, after installing the service, to ask the user which folder he wants to share.
<feature name="http://xmlns.opera.com/fileio">
<param name="folderhint" value="home" />
</feature>
home is the user folder on the disk (such as Documents and Settings / user). Other examples of folders are in the API. However, just the path to the folder will not be given to you. Cross-platform, you see.
This is where the difficulties begin. The folder is shared, but there are no files in it (!). Although from the manuals to Unite indirectly follows the opposite. Besides the fact that the files will have to be shared, there is another difficulty. Due to the instability of the service, it is impossible to rely on the fact that a file that was once shared will be shared during the session. Therefore, I recommend to process access to files as requests are received. The user requested the file - shared, downloaded - removed the status of the shared. Another complication is, unfortunately, the unshareFile and shareFile functions that throw out a fatal error, if you slip them into an already unshared file or shared file, respectively. Those. Double share not something that does not work, you also get a non-working server.
The shared folder has a shared alias. In addition to it, there is an application (service folder), storage (a folder dedicated to service data). Let's connect shared to our server:
window.onload = function () {
webserver = opera.io.webserver;
if (webserver)
{
opera.io.filesystem.mountSystemDirectory('shared');
webserver.addEventListener('shared', download, false);
webserver.addEventListener('_index', showEntryList, false);
}
}
After turning on the server and service, we will mount our shared folder. Considering the above about the instability of the Opera, we suggest that the server manually process the request.
Now create the download function itself:
function download(e) {
var req = e.connection.request;
var res = e.connection.response;
var mp = opera.io.filesystem.mountPoints;
var filePath = fullPathWithoutServiceName(req.uri);
var file = mp.resolve(filePath);
if(file.exists) {
if(file.isFile) {
opera.io.webserver.shareFile( file, filePath );
res.closeAndRedispatch();
opera.io.webserver.unshareFile(file);
}
if(file.isDirectory) {
//deep inside... folders, folders, folders. total commander :)
showEntryList(e);
res.closeAndRedispatch();
}
}
else {
res.closeAndRedispatch();
}
res.close();
}
We will not stop here for a long time, everything is clear about the names of API functions. Mount the path after the name of the service in urla. fullPathWithoutServiceName is my samopisnaya function, parsit url. If a file really exists on it, check if it is a folder or just hang out here. In the case of a folder, we pass the action to our showEntryList handler, which simply lists the files and folders in the directory. In the case of a file, we share it, transfer the reins to the server, and returning, we subtract the rights from the file. Before displaying the file names, please note that all characters are encoded there and you need to wrap them in the unescape function before outputting them. Otherwise, in the simplest case, instead of the spaces of the file name, the user will see% 20.
Using the closeAndRedispatch function, we return its functions to the server and it continues its work. So I configured work with multi-level folders and files.
Version update

There are no similar notification mechanisms for updates, as in Firefox. Alternatively, you can use the information about the latest version of the application through the DOAP file, which lies on the site of United. So, for example, if the service has the address:
unite.opera.com/service/322Then its DOAP file is located at:
unite.opera.com/service/doap/322Since this is plain XML, you can parse it, check the version and suggest it in a popup window. Here is an example of processing:
function getLastVersion() {
try {
var req = new XMLHttpRequest();
req.onreadystatechange = function(x) {
if (this.readyState == 4) {
if(this.status == 200) {
var xml = this.responseXML;
var release = xml.getElementsByTagName('release').item(0);
var version = release.getElementsByTagName('Version').item(0);
var revision = version.getElementsByTagName('revision').item(0).firstChild.data;
var lastCheckedVersion = getLastCheckedVersion();
if(revision && lastCheckedVersion && revision!=lastCheckedVersion) {
widget.showNotification("New version "+revision+" is available. Click here to update.", function() {
widget.openURL(globalSettings.url);});
setLastCheckedVersion(revision);
} } } };
req.open('GET', globalSettings.doap);
req.send(null);
}
catch(e) {
}
}
function getLastCheckedVersion() {
return widget.preferenceForKey('last_checked_version');
}
function setLastCheckedVersion(version) {
widget.setPreferenceForKey(version, 'last_checked_version');
}
Please note that access to external resources is used, you need to correct the config.xml, as described earlier.
The
URL unite.opera.com/service/322 can
also be obtained automatically in theory, by attaching the service directory and digging into the config.xml file — the URL will be entered there in the form of an ID. But I had a lot to dig into this, if someone writes and opens the code - I will be happy :) And so, at the time of writing this function, I already knew this URL.
Debag

Error handling is still zero. Often the server is cut off from fatal errors, often these errors are unclear which means that there are no manuals, and abstract names speak little. You can’t make any alerts on server javascript, the only way out to people is the function opera.postError ('error'); which reports an error to the Opera developer console.
Ratings on Unite.opera.com

Fucking done ... Firstly, there is no way to check the version number of the application. By default, after verification, the service receives version 1.0. When updating, you can only indicate that the changes are minor (version 1.1) or significant (version 2.0). Please note, with this, the race rating for the last 7 days is reset and you fall sharply down to the last place. So the developer has no incentive to upload new ads, because no one will notice the next version, in the ranking you will be at zero. An application that has not been updated for a hundred years can always hang around in the top and not leave it at the expense of hamsters. So at the moment I recommend that you do not update the version until a significant number of errors are fixed.
Stream media

The result of my efforts was the service Stream media, which allows you to do something like YouTube for your people once or twice. You can play video and audio. Of course, if they were limited only to MP3 and FLV formats, everything would be cross browser friendly. But the majority stores video in AVI, etc., therefore cross-browser compatibility and cross-platform issues arise. Here I can not help with anything, and so everything is maximally “cross” due to the jQuery plugin. However, with AVI in Windows Media Player you will have to wait until it completely downloads the file. The maximum that I can offer, either convert to FLV, then the flash player will start, or download the file to the client before viewing.
You can download it here:
unite.opera.com/service/322So it was a little messy, I had to climb into the field of activities of the official manuals, but without a repetition of the past, I could not tell about the features that are not described on the website of Unite. If you have questions, write, try to figure it out together.
PS I think it makes no sense to monetize the service. Therefore, I hope that soon there will be a better version with design, I communicate with one person on this topic, on altruistic foundations :)