Hardware and software setup

Development of the server part for mobile applications. Mobile Application Development: Synchronization with the server Development of the server side of the application

488 Part IV. Ajax in examples

bots, and invokes the server, which will dynamically generate response data based on the submitted query string value. The first parameter of the LoaciXMLXSLTDoc() function is the URL of the PHP page that generates the XML document, concatenated with a query string generated by the HTML form O field value reference. The second parameter is the name of the © XSLT file used in the XML data transformation. The third parameter required by the LoadXMLXSLTDoc() function is the ID of the div element in which to place the search results. The identifier is the string name of the output element, not an object reference; in this case, the string "results" is used as the identifier.

In the next step, we use DOM methods to add an indicator image to the Web page. An image element is created © and the image source attribute is set to O. This created element is added to the results div element ©. Thus, when our function is called from the form's onsubmit event handler, an image file is placed on the page. In general, it is important for the user to create a visual feedback- a message or image, - indicating that processing is in progress. This prevents the user from repeatedly clicking on the handicap submit button thinking that nothing is happening (remember, the Ajax process is "invisible").

The last step is to call the LoadXMLXSLTDoc () ® function, which initiates the process of sending information to the server. The LoadXMLXSLTDoc() function described in Section 12.4 handles the call to the ContentLoader() object that requests documents from the server. By setting the output position as a parameter (rather than hard-coding the value in the LoadXMLXSLTDoc() function), we can reuse this function on one page without requiring for separation functionality adding multiple procedures or if statements. Therefore, we redirect the results of various search queries to different parts of the page. However, before we do all that, let's see how to create XML and XSLT documents on the server.

12.3. Application backend code: PHP

V In this section, we will create a dynamic XML document using PHP, a popular open source scripting language (as you know, the Ajax framework is compatible with any server-side language or platform). The XML document is generated dynamically from the result set received in response to a client query against the database. In addition, we'll show you how to create a static XSLT document that resides on the server and is retrieved every time a dynamic file is requested. Both of these documents are returned to the client independently when the ContentLoader object is requested in two separate requests (Listing 12-7). The XSLT transforms our dynamic XML document on the client side and creates an HTML table that is displayed to the user.

Chapter 12 Live Search Using XSLT 489

12.3.1. Creating an XML Document

Since we are using XSLT, we need a structured XML document that is a simple record of information so that the XSL file can perform a standard transformation. In this project, we will create a dynamic XML file when a client requests a PHP file.

XML structure development

Before we start creating the XML file, we need to create a template for it. This pattern should reflect the structure of the data returned by the search. In the chosen task statement (phone book), we will return the company name, contact name, country, and telephone number. Listing 12-3 shows a basic XML template that contains four fields.

Listing 12.3. Basic XML File

company name contact name country name phone number

The first element is phonebook. The next one is an entry element containing subelements with all the details that are associated with all the contact numbers found in the request. If we have five results, there will be five entry elements in the XML document. The name of the company is displayed in the company element. In addition, we have added the name of the contact person, the name of the country and the phone number. We are not only limited specified fields; Fields can be added or removed depending on what information you want to display.

If no results are found, instead of displaying a warning message, you can create an element that displays this information to the user. STO will make it easier for us to return the result to the user and will not require additional code in the client side of the application. The code in Listing 12-4 is pretty much the same as in Listing 12-3, but this time we're entering text into the XML elements we want to show the user to indicate that no results were found.

Listing 12.4. XML file with no results

no results

// O Displays "No R e s u l t s" instead of company name N/A

490 Part IV. Ajaxin examples

// © Remaining fields are replaced by "N/A"

N/A

N/A

With this code, we display a single line to the user telling the user that the requested information is missing. The company O descriptor displays information indicating that there are no results. Other descriptors 0 tell the user that there is no information. If we don't want to display the text "N/A" ("not available"), we can add a non-breaking space instead, which will show the table cells. If we had not added any information at all, the cells in the table would not have appeared.

As you can see, the XML format has a very simple structure. If this XML file were static, it would be relatively easy for the user to add a new subscriber to the file. Since it is dynamically generated, we need a loop that creates an XML document from the result set.

Creating a Dynamic XML Document

As always, we create an XML document on the server. In keeping with the policy of using different server languages ​​in the examples, the server code for this chapter is written in PHP. Recall once again that the Ajax framework can be created using any server-side language, and we will only describe the principle of implementing server-side code without going into details. So, Listing 12-5 shows the code for the server side of the application. The code takes a query string parameter and generates a lot of database query results. We then iterate through the set of results, creating an element in the XML file, as shown in Listing 12.4, for each phone number returned in response to the query.

Listing 12.5. phoneXML.php script: generating an XML document on the server

// About Declare MIME Type header("Content-type: text/xml"); echo("\n");

// © Connect to database

$db = mysql_connect("localhost", "ajax", "action"); rnysql_select_db("ajax",$db);

$result = mysql_query("SELECT *

FROM Contacts WHERE ContactName like "%". // © Fill out the request

$_GET["q"] ."%"",$db); ?>

// O Check results

if ($myrow = mysgl_fetch_array($result)) ( do (

// © Loop through the set of results

Chapter 12 Live Search with XSLT 491

001">

)while ($myrow - mysql_fetch_array($result)); )else(

phone book, or issue the message "No Results" 0.

12.3.2. Create an XSLT document

Using XSLT, our XML file can be converted into a beautiful HTML table with a couple of lines of code. An XSLT document allows template matching if it is necessary to display data in any desired format. Template matching uses a template structure to display data. At the same time, we pass through the nodes of the tree

492 Part IV. Ajax in examples

source using XSLT. An XSLT document takes a structured XML file and converts it into a format that is easy to view, update, and modify. In our case, the XSLT document is statically defined.

XSLT structure

An XSLT transformation contains rules for translating a source tree into a destination tree. The entire XSLT process is to match against a template structure. When the elements of the source tree match the given structure, the destination tree is created according to the document template.

The structure of the destination tree does not have to be related to the structure of the source tree. Therefore, the original XML file can be converted to any desired format. It is not necessary to use only a tabular view of the dataset.

An XSLT transformation is called a style sheet because it defines the styling of the resulting tree. The style sheet contains template rules, which are composed of two parts. The first part is the template structure against which the nodes of the source tree are compared. Having found a match, the XSLT processor uses the second part - a template containing descriptors for building the source tree.

Create an XSLT Document

Generating an XSLT transformation for a given project is relatively simple. Since we're going to get a table, no fancy pattern matching is required; we simply sequentially go through all the element nodes of the source tree. Below we will develop a template that generates an HTML table with four columns. The corresponding XSLT file for this project is shown in Listing 12-6.

i Listing 12.6. XSLT file

"ISO-8859-l"?>

// О Set XML version and encoding

// © Set the XSLT namespace"http://www.w3.org/1999/XSL/Transform">

// © Set template rules

// About Add a table element

// © Create a title bar

Chapter 12 Live Search Using XSLT 493

// 0 Sequentially go through the elements of the phone book

select="phonebook/entry"> // © Format the output

company contact Country Phone

When you create an XSLT transformation, you must specify the XML O encoding and version, and specify the XSLT © namespace. The namespace defines the rules and specifications that a document must conform to. Elements in the XML namespace are recognized in the source document but are not recognized in the result document. The xsl prefix is ​​used to define all of our elements in the XSLT namespace. Next, you can set up a template rule - look for a structure / © that matches the entire document.

Now we can start creating a table template that displays our results. We add a table descriptor O that maps an identifier to the table. This is followed by a table header row © containing the names of the columns that indicate to the user what information is contained in the table.

Sequentially passing through the set of nodes of the source tree, we get the remaining rows of the table. In this case, a for-each © loop is used, in the process of processing entries, issuing nodes located in phonebook/entry.

Since we are sequentially traversing the document tree, we need to choose column values. To select values ​​from nodes, the value-of to operator is used to extract the value of the XML element and add it to the output stream of the transformation. To specify the XML element whose text we want to extract, use the select attribute with the element name. Once you've generated the XSLT file and created the code to dynamically generate the XML document, you can complete the JavaScript code and see how combining an XSLT transformation with a structured XML file produces an easy-to-browse table.

In the next step, we return to the client side again, retrieving the files we just created with the HTTP response.

Development of the server part of the client- server application starts with architecture design. A lot depends on the architecture: from the extensibility of the application to its performance and ease of support/maintenance.

First of all, you should determine how the data will be placed on the server and how requests coming from the client will be processed. It is also necessary to think over the organization of server-side data caching.

It is necessary to decide on data exchange protocols and data transfer formats.

API (application programming interface) - interface application programming. In a more understandable language, this is a set of requests to the server, which the latter understands and can give the correct answer. The API defines the functionality of the server logic, while the API allows you to abstract away how this functionality is implemented. In other words, the API is a necessary part of the overall client-server infrastructure.

Compare JSON and XML. Give an example of protocols depending on the type of application.

Multithreading

One of the key aspects in modern programming is multithreading. With the help of multithreading, we can separate several threads in the application that will perform various tasks at the same time.

Multithreading is a property of the platform (for example, operating system, virtual machine etc.) or application, which consists in the fact that a process spawned in the operating system can consist of several threads executing "in parallel", that is, without a prescribed order in time.

The essence of multithreading is quasi-multitasking at the level of one executable process, that is, all threads are executed in the address space of the process. In addition, all threads of a process have not only a common address space, but also common file descriptors. A running process has at least one (master) thread.

Multithreading (as a programming doctrine) should not be confused with either multitasking or multiprocessing, even though operating systems that implement multitasking tend to implement multithreading as well.

The advantages of multithreading in programming include the following:

Simplifying the program in some cases by using a common address space.

Less time spent on creating a thread relative to the process.

Improving process performance by parallelizing processor calculations and I/O operations.

Flow(thread) is a managed unit of executable code. In a thread-based multitasking environment, all running processes necessarily have a main thread, but there can be more. This means that a single program can run multiple tasks asynchronously. For example, editing text in text editor at print time, as the two tasks run on different threads.

On a conventional processor, thread management is handled by the operating system. The thread is executed until a hardware interrupt occurs, system call or until the time allotted for it by the operating system expires. After that, the processor switches to the operating system code, which saves the state of the thread (its context) or switches to the state of another thread, which is also allocated time for execution. With such multithreading, it is enough a large number of CPU cycles are spent on operating system code that switches contexts. If thread support is implemented in hardware, then the processor itself will be able to switch between threads, and in the ideal case, execute several threads simultaneously for each clock cycle.

– Temporary multithreading (single thread)

– Simultaneous multithreading (multiple threads at the same time)

Multithreading, as a widespread programming and code execution model, allows multiple threads to run within a single process. These threads of execution share the resources of a process, but can also run on their own. The multithreaded programming model provides developers with a convenient abstraction of parallel execution. However, perhaps the most interesting application of the technology is when it is applied to a single process, which allows its parallel execution on a multiprocessor system.

This advantage of a multi-threaded program allows it to run faster on computer systems, which have multiple processors, a processor with multiple cores, or on a cluster of machines - due to the fact that program execution threads naturally lend themselves to truly parallel execution of processes. In this case, the programmer needs to be very careful to avoid race conditions and other non-intuitive behavior. In order to properly manipulate data, threads of execution must frequently go through a rendezvous procedure to process the data in the correct order. Threads of execution may also need mutexes (which are often implemented using semaphores) to prevent shared data from being modified at the same time or read during the modification process. Careless use of such primitives can lead to a deadlock.

Another use of multithreading, even for uniprocessor systems, is the ability for an application to respond to input. In single-threaded programs, if the main thread of execution is blocked by the execution of a long-running task, the entire application may be in a frozen state. By moving such long-running tasks to a worker thread that runs in parallel with the main thread, it becomes possible for applications to continue to respond to user input while the tasks are running in background. On the other hand, in most cases multithreading is not the only way to keep a program responsive. The same can be achieved through asynchronous I/O or signals in UNIX.

There are two types of multitasking: process-based and stream-based. The differences between process-based and thread-based multitasking are as follows: process-based multitasking is organized for the parallel execution of programs, and thread-based multitasking is for the parallel execution of individual parts of one program.

There are two types of streams:

Foreground threads or foreground. By default, every thread created through the Thread.Start() method automatically becomes a foreground thread. This type Threads provide protection for the current application from terminating. The common language runtime will not stop the application until all foreground threads have completed.

Background threads. This type threads, also known as daemon threads, are treated by the common language runtime as extensible execution paths that can be ignored at any time. Thus, if all foreground threads are terminated, then all background threads are automatically killed when the application domain is unloaded. To create background threads, you must set the IsBackground property to true.

Tell about the states of threads: running, suspended, running, but waiting for something.

Thread synchronization problem and shared resources.

Thread interaction

In a multithreaded environment, there are often problems associated with the use of the same data or devices by parallel executing threads. To solve such problems, thread interaction methods such as mutexes, semaphores, critical sections, and events are used.

Mutex is a synchronization object that is set to a special signaled state when not occupied by any thread. Only one thread owns this object at any time, hence the name of such objects (from English mutually exclusive access - mutually exclusive access) - simultaneous access to a shared resource is excluded. After all necessary actions, the mutex is released, giving other threads access to the shared resource. An object can support recursive capture a second time by the same thread, incrementing the counter without blocking the thread, and then requiring multiple releases. Such, for example, is the critical section in Win32. However, there are some implementations that do not support this and cause the thread to deadlock when attempting a recursive capture. This is FAST_MUTEX in the Windows kernel.

semaphores represent the available resources that can be acquired by multiple threads at the same time until the resource pool is empty. Then additional threads must wait until the required amount of resources is available again. Semaphores are very efficient because they allow concurrent access to resources.

Events. An object that stores 1 bit of information “signaled or not”, on which the operations “signal”, “reset to an unsignaled state” and “wait” are defined. Waiting on a signaled event is the absence of an operation with an immediate continuation of the execution of the thread. Waiting on an unsignaled event causes the execution of a thread to be suspended until another thread (or the second phase of an interrupt handler in the OS kernel) signals the event. It is possible to wait for several events in the "any" or "all" modes. It is also possible to create an event that is automatically reset to an unsignaled state after waking up the first - and only - waiting thread (such an object is used as the basis for implementing the "critical section" object). Actively used in MS Windows, both in user mode and in kernel mode. There is a similar object in the Linux kernel called kwait_queue.

Critical sections provide synchronization like mutexes, except that the objects representing critical sections are accessible within the same process. Events, mutexes, and semaphores can also be used in a single-process application, however, implementations of critical sections in some operating systems (for example, Windows NT) provide a faster and more efficient mechanism for mutually exclusive synchronization - the acquire and release operations on the critical section are optimized for case of a single thread (no contention) in order to avoid any system calls leading to the OS kernel. Like mutexes, an object representing a critical section can only be used by one thread at a time, making them extremely useful in restricting access to shared resources.

Condition variables(condvars). Similar to events, but they are not objects that occupy memory - only the address of the variable is used, the concept of "contents of the variable" does not exist, the address of an arbitrary object can be used as a condition variable. Unlike events, setting a condition variable to the signaled state has no consequences if there are currently no threads waiting on the variable. Setting an event in a similar case entails storing the "signaled" state within the event itself, after which subsequent threads that wish to wait for the event continue execution immediately without stopping. To make full use of such an object, the operation “release the mutex and wait for the condition variable atomically” is also necessary. Actively used in UNIX-like operating systems. Discussions about the advantages and disadvantages of events and condition variables are a prominent part of the discussions about the advantages and disadvantages of events and condition variables. disadvantages of Windows and UNIX.

I/O Completion Port(IO completion port, IOCP). Implemented in the OS kernel and accessible through system calls, the "queue" object with the operations "put the structure to the tail of the queue" and "take the next structure from the head of the queue" - the last call suspends the execution of the thread if the queue is empty, and until no other thread will make the put call. The most important feature of IOCP is that structures can be placed in it not only by an explicit system call from user mode, but also implicitly inside the OS kernel as a result of the completion of an asynchronous I / O operation on one of the file descriptors. To achieve this effect, you must use the "associate a file descriptor with IOCP" system call. In this case, the structure placed in the queue contains the error code of the I / O operation, and also, in the case of success of this operation, the number of actually entered or output bytes. The implementation of the completion port also limits the number of threads executing on a single processor/core after a structure is received from the queue. The object is specific to MS Windows, and allows the processing of incoming connection requests and data chunks in the server software in an architecture where the number of threads can be less than the number of clients (there is no requirement to create a separate thread with resource costs for each new client).

Thread Pool

Tell about the thread pool

Development of the server side of the application

Introduction

Internet presence has become a necessity for modern companies. Without this, it is impossible to build a full-fledged interaction with customers. Often, to solve such a problem, they resort to creating client-server applications. Each of them consists of the client part and Back-end. The last term means server part applications. If in the future you need to independently change the content of the mobile program, then the Back-end must be created with especially high quality. Appomart guarantees the fulfillment of the assigned tasks in accordance with the requirements. Therefore, when ordering the creation of server applications, you can be sure of the proper result.

What is Back-end for?

Developing client-server applications involves the creation of two parts. The first, Front-end, accepts requests from users. It is visible from the screens mobile devices clients. The second, the server application, processes the received requests and acts as an administrative panel. Databases and program logic are stored here. Nothing will work without this. client-server application. In fact, the back-end is the heart of the program. This is the intelligence that is responsible for processing customer requests, the speed of the application. Therefore, it is important that the architecture of the server application is thought out to the smallest detail, so that even highly loaded services work smoothly and quickly.

How to choose a programming language?

In preparation terms of reference(parts of the working documentation for the project), the architect designs the database system and links, describes the objects and their properties, and develops the necessary server methods (requests that mobile applications will "use" when contacting the server).

The Importance of Documentation and Abandoned Projects

Appomart is quite often contacted by customers who have been "abandoned" for one reason or another by other contractors. And we take someone else's, sometimes even incorrectly working project, we carry out its audit and subsequent revision and support. In the process of studying source code and materials received from the customer, we are faced with the fact that many developers intentionally do not document the server methods in order to bind the client to themselves, due to the incommensurability of the labor costs of transferring the project to support another developer, due to the lack of documentation for the server part, and sometimes simply because for unprofessionalism. This fact, unfortunately, is not only sad but also widespread. The customer, in this case, needs to pay for the development of documentation for an existing project, as well as an audit of the source code, before it will be possible to judge the efficiency, convenience and expediency of supporting the project. Appomart maintains electronic documentation of back-end methods in a format supported by Postman and Swagger for later use.

How to check the contractor before signing the contract?

We urge you to carefully choose a contractor, and focus not only on a tempting price, but also on the list of documents that you will receive along with the project, as well as the conditions for transferring the source code, and covering the code with comments, database schemas (be it Mongo DB or MySQL ). The key to success, as a rule, is competent working documentation, which clearly indicates the requirements for the materials transferred to you upon completion of each stage of the work.

Development Features

PHP for the back end

The creation of the server side of applications (not to be confused with servers as "hardware" or computers, since we are talking about the software side) requires specific professional skills and knowledge of the programming language that is used on the server side. If we look at examples of client-server applications, we can see that PHP is popular. It is the undisputed leader in server application development. More than half of the sites in the world are written in this language in one configuration or another. PHP is easy to develop and maintain, and besides, there are special frameworks to speed up development in PHP.

Framework

Framework( software platform) - used to organize and increase levels of abstraction, which makes the project more flexible and scalable. However, it should be understood that the framework must be chosen correctly, based on an in-depth analysis of the project's working documentation, without which it is impossible to develop a quality product.

Delphi, JAVA, Python

There are other languages ​​that are used to create the Back-end. So, created in Delphi environment server applications. With its help, the program gets improved debugging, it is also easy to form in the environment unique programs, provided visual creation, which makes it possible to make a beautiful, understandable and user-friendly interface. Java server applications have also gained popularity. These are easily complemented, easily executed on any platform and have a decent level of security. Another popular language is Python. Server applications with its help are created quickly, simply, without serious expenses.

Spreading

Creation of client-server applications is in demand in the corporate environment. Often similar programs used for working groups or creating information systems within the enterprise. The vast majority mobile applications to maintain communication with the client also has a similar architecture. The popularity is due to the fact that the use of server capabilities allows you to ensure the control and integrity of the system, while reducing network load.

We will create a client-server application for Android, iOS with high quality and on time

Turnkey development

Appomart programmers are experienced and qualified to implement tasks of various levels. We are equally good at implementing social networks, high-load business projects, or a software part for small startups. If necessary, we will create the client part of the application under Android control, iOS according to existing needs, requirements.

Back-end at Appomart

Our programmers work with different technologies and do it equally well. At Appomart, you can order a client-server application in Java, PHP, and Node.JS. System requirements are analyzed for each of the projects individually, which allows to ensure the optimal performance of the program. We will create a Java, PHP and Node.JS client-server application from scratch or take an existing one to support for improvements and updates. If you are interested in developing a new server part or supporting an existing one, leave a request to get a detailed calculation of the cost of work and options for cooperation.

back side mobile clients- server.

Additional requirements depend on the specifics of the application:
server scalability - for SaaS, social applications, where a large flow of visitors is ideally expected, this condition is mandatory. For business applications where there are restrictions on the number of users or the number is predicted, this property is not required;
interactivity: a number of applications need to be provided with a notification mechanism - to inform the application (user) about the occurrence of certain events, send a message to the user. This property should have, for example, an exchange system or automatic dispatcher Taxi.
open API: it is assumed that third-party developers can use the functionality of the system through a documented protocol. After all, the client can be both a mobile and an external server application.
other requirements...

Team
The composition of the project team for system development would ideally be as follows:
project manager: manages, controls the project, interacts directly with the customer;
server application developer: develops business logic server, database, network protocol;
admin app developer: develops web application, user interface to configure and manage the server application;
Android client application developer;
iOS client application developer;
client application developer for...
tester: tests the admin application and client applications.

The attentive reader will notice that in the case of writing a server application with GUI, for example, on HTML5, you can save. In this case, the development of client applications is not required - the user interface is provided by the browser. This article does not consider such a case, we are talking about the development of "native" (native) applications for mobile devices.

I had a chance to work in a team with a full complement, but be realistic - not always human resources and budget allow you to assemble such a team. And sometimes the roles have to be combined: project manager + server application developer, client application developer + tester.

Technologies, tools, libraries
To develop a mobile client server, I usually use the following stack of “free” technologies:
Apache Tomcat is a servlet container;
MySQL - DBMS;
Subversion is a version control system;
Maven - a framework for automating the assembly of projects;
JUnit - will provide ;
Apache Log4j - logging library;
Jenkins is a continuous integration system;
Hibernate - ORM (settings, configuration in properties, xml files and annotations)
hibernate-generic-dao - DAO implementation from Google, implements the main methods for working with database data, simplifies the implementation of filtering and sorting in methods;
- implementation of authentication and authorization (security), a container of services and beans (configuration in xml files and annotations), we also use it when creating tests.

Depending on the specifics of the system and the requirements for it, I use one of 2 options for implementing the data exchange protocol.
When cross-platform, speed, simplicity, efficiency, scalability, open API are required, then I take Jersey - the implementation of REST Web services (RESTful Web services). This library allows you to use data serialization in JSON or/and XML format. REST configuration is maintained through annotations. For the exchange with mobile devices, the JSON format was taken due to the fact that it has a simpler implementation on the client side (for this reason we do not use “classic” Web services), a smaller amount of traffic is generated. Jersey allows you to tune in to the most appropriate "kind" of JSON.
Otherwise, if you need cross-platform, high performance, simplicity, efficiency, interactivity, then I take
Apache MINA is a framework for creating network applications,
Google protobuf is a structured data encoding and decoding library. The data structure is defined by the *.proto header files, the compiler generates Java classes from them (it is also possible to generate them for other programming languages: C++, Objective-C, etc., which provides the cross-platform property);
java.util.concurrent - use the standard package.
This option can be scaled, but it needs to be planned at the design stage at the architecture level, taking into account the business logic.

Consider a hypothetical task using the example of choosing technologies for a real SaaS service - “Auknem Services Auction”, which allows people to place an order for the performance of the required services or works, and organizations, in turn, leave their offers for them. We take all the basic requirements by default. In view of the fact that registration in this system is free and free, it is definitely necessary to add scalability to them. What about interactivity? It would be great to inform contractors (performers) about the creation of new orders, and inform customers about the proposals received at the same moment in the application, and not just by e-mail. Based on this, we will take Apache MINA, Google protobuf for implementation. We look at the following property - open API. The service is public, so let's assume that external developers may be interested in integrating with it. Wait! Not so simple. The Apache MINA-based protocol is quite implementation-dependent, and integration without knowing the nuances is by no means transparent. In such a situation, you will have to weigh which factor is more important and make a choice.

Conclusion
It would be interesting for me to know what technologies, libraries you used when developing a mobile device server or similar systems? Everything changes, nothing lasts forever, at every level there are alternatives with their own advantages and disadvantages: MySQL -
Liked the article? Share with friends!
Was this article helpful?
Yes
Not
Thanks for your feedback!
Something went wrong and your vote was not counted.
Thank you. Your message has been sent
Did you find an error in the text?
Select it, click Ctrl+Enter and we'll fix it!