Author Archives: admin

Open source: copyleft vs licencias permisivas

[Fuente: http://blogthinkbig.com/licencias-open-source/]

El software libre cumple con la premisa de que el código puede ser utilizado y modificado sin permiso, pero debe distribuirse bajo las mismas licencias open source: copyleft.

Gracias a la revolución tecnológica que ha permitido que los bienes culturales y el conocimiento se independicen de los formatos físicos y puedan ser compartidos por cualquiera, en cualquier momento, en prácticamente todo el mundo, ha nacido y se ha desarrollado la llamada cultura libre o abierta.

El término software libreacuñado por Richard Stallman en 1985 durante la fundación del proyecto GNU, se atribuye a cualquier software que asegure que el usuario final tendrá la libertar de usarlo, estudiarlo, compartirlo y modificarlo sin restricción alguna. Las licencias que cumplen con esta definición implican que el código fuente como tal, en caso de ser redistribuido, tiene que hacerlo bajo la misma licencia, por lo tanto todos los derivados heredan las mismas condiciones. Eso es el concepto de Copyleft, diseñado para asegurar la “libertad para todos”, pero esas condiciones son en realidad poco permisivas en ciertos aspectos. Veamos por qué.

Copyleft

Ya hace tiempo que el copyleft se saltó los márgenes del software hasta alcanzar muchos otros ámbitos de la producción intelectual. Aunque muchos no entienden bien de qué se trata, el copyleft forma parte de todo un movimiento cultural y político liderado por los trabajadores intelectuales del mundo.

licencias open source - licencias open source - licencias open source - licencias open source - licencias open source - licencias open source - licencias open source

En el ámbito de la programación, copyleft define un concepto jurídico. Señala que todo programa licenciado como software libre tendrá que seguir siendo tal en las obras derivadas con o sin modificaciones. El copyleft garantiza así que nadie pueda apropiarse de un programa de software libre, al menos de acuerdo a la legalidad.

El copyleft ha pasado al terreno de los bienes culturales para dar al autor la posibilidad de elegir, con plena libertad, el modelo de distribución y explotación de sus obras. Estas libertades son: de copia y distribución no comerciales (para muchos la condición mínima imprescindible para considerar un bien cultural como libre), de distribución comercial, de modificación y de obra derivada, y obliga a que la obra se comparta bajo la misma licencia que el original.

No es suficiente con decir que el proyecto es libre, sino que los creadores deben elegir una licencia clara para evitar la confusión, o desanimar a alguien sobre usar su trabajo por no saber exactamente bajo qué condiciones ha sido liberado.

Licencias permisivas

Las licencias de software de tipo copyleft suelen ser bastante complicadas. AGPL, GPL, LGPL, MPL, etc., todas usan un montón de lenguaje complejo, que puede terminar por ocupar más tiempo al desarrollador el hecho de estudiarlas y comprenderlas que dedicarse de verdad a trabajar en su proyecto. Además de esto varias licencias son incompatibles con otras, principalmente porque el copyleft exige que todo derivado se comparta bajo la misma licencia, modificado o no. Por esta razón, para simplificar la tarea de elegir una licencia, muchos acuden a algunas más permisivas.

Al usar una licencia permisiva se facilita a otros reutilizar el trabajo. Este tipo de licencias sólo se aplican al código fuente licenciado, y no hace intentos de interferir o aplicar condiciones sobre nada más, por lo que ahorran el dolor de cabeza de definir lo que es una obra derivada, ya que no se aplica siquiera a ellas. Usar al mismo tiempo muchos proyectos open source con licencias restrictivas, y llevar la cuenta de cuáles son compatibles unas con otras, puede ser una odisea terrible. Ejemplos conocidos de licencias permisivas son la licencia MIT y la BSD.

Creative Commons

Una alternativa a las licencias complejas es el uso de licencias Creative Commons, aunque una vez que se introduce el elemento SA (Share Alike), que significa que las obras derivadas deben compartirse de la misma manera que la original, o con una licencia comparable, se cae en el mismo problema. No obstante, aún con esto no cabe duda de que Creative Commons simplifica muchísimo el proceso y le ahorra al creador la lectura de decenas de párrafos llenos de jerga legal a veces difícil de entender, además son aplicables internacionalmente, fáciles de elegir y mucho más flexibles y humanas que el sistema de copyright.

[Fuente: http://www.ite.educacion.es/formacion/materiales/155/cd/modulo_1_blogs_integraciondocumentos/aadir_licencia_cc_a_un_blog_blogger.html]

Añadir licencia CC a un blog Blogger

Si estás interesado/a en situar una licencia CC (Creative Commons) en la barra lateral de tu blog puedes seguir los siguientes pasos:

  • Haz clic en la opción de menú Licencias.
  • Pulsa en el enlace Escoger una licencia.
  • En la pregunta ¿Quiere permitir usos comerciales de su obra? marca la opción deseada. Por ejemplo: No.
  • En la pregunta ¿Quiere permitir modificaciones de su obra? señala una respuesta. Por ejemplo: Sí, mientras se comparta de la misma manera.

¿Permitir modificaciones de su obra?

  • En la lista desplegable Jurisdicción de su licencia elige el país de procedencia. También es posible añadir información adicional de la obra pero en este caso no lo haremos.
  • De la combinación de las respuestas emitidas surgirá la licencia antes descrita como: Reconocimiento-No comercial-Compartir Igual.
  • Clic en el botón Escoja una licencia.
  • Selecciona el modelo de icono que deseas incluir en tu página HTML. Observa que debajo se muestra la vista previa que tendrá la licencia.

Estilo de botón de licencia

  • Clic derecho sobre el cuadro que muestra el código HTML y elige Copiar.

copiar código

  • Ahora vamos a crear en el blog un gadget en la barra lateral que muestre la licencia elegida a los lectores.
  • Desde el Escritorio de administración del blog elige la opción Diseño en la columna izquierda.

Diseño

  • Haz clic en el enlace Añadir un gadget correspondiente a la barra lateral.

Añadir un gadget

  • Clic en el botón Añadir (+) para incorporar a la barra lateral un gadget del tipo HTML/Javascript.

Añadir HTML/Javascript

  • En el cuadro de diálogo Configurar HTML/Javascript introduce como Título el texto Licencia Creative Commons y en el cuadro Contenido haz clic derecho y selecciona Pegar para pegar el código HTML copiado desde Creative Commons. En este código puedes modificar el texto: “Esta obra …” por “Este blog…”

Configurar HTML/Javascript

  • Clic en el botón Guardar.
  • Si deseas modificar de posición arriba/abajo del gadget basta con pulsar y arrastrar uno respecto al otro sobre la edición de la barra lateral.

Arrastrar para mover gadget

  • Clic en el botón Guardar disposición que se muestra en la esquina superior derecha de gestión del blog.
  • Para ver el resultado final haz clic en el botón Ver blog. En la columna lateral se mostrará un gadget con el icono del tipo de licencia elegido.

Icono del tipo de licencia

  • Cuando el lector hace clic en el icono o en el enlace textual se mostrará la página de Creative Commons con las condiciones de la licencia elegida.

Facebook Login: Access Tokens

[Fuente: https://developers.facebook.com/docs/facebook-login/access-tokens/ ]

Cuando alguien se conecta con una aplicación que utiliza Facebook Login, la aplicación podrá obtener un access token que proporciona un acceso temporal y seguro a las APIs de Facebook.

Un token de acceso es una cadena opaca que identifica al usuario , la aplicación o la página que puede ser utilizada por la aplicación que invoca llamadas sobre el API de facebook. Los tokens de acceso son obtenido por varios métodos, que veremos más adelante. El token incluye información sobre cuando el token expira y qué app generó el token. Debido a comprobaciones de privacidad, la mayoría de las API calls sobre Facebook necesita incluir un access token. Hay distintos tipos de acceso que soportan todos los casos de uso:

  • User Access Token – The user token is the most commonly used type of token. This kind of access token is needed any time the app calls an API to read, modify or write a specific person’s Facebook data on their behalf. User access tokens are generally obtained via a login dialog and require a person to permit your app to obtain one.
  • App Access Token – This kind of access token is needed to modify and read the app settings. It can also be used to publish Open Graph actions. It is generated using a pre-agreed secret between the app and Facebook and is then used during calls that change app-wide settings. Youobtain an app access token via a server-to-server call.
  • Page Access Token – These access tokens are similar to user access tokens, except that they provide permission to APIs that read, write or modify the data belonging to a Facebook Page. To obtain a page access token you need to start by obtaining a user access token and asking for the manage_pages permission. Once you have the user access token you then get the page access token via the Graph API.
  • Client Token – The client token is an identifier that you can embed into native mobile binaries or desktop apps to identify your app. The client token isn’t meant to be a secret identifier because it’s embedded in applications. The client token is used to access app-level APIs, but only a very limited subset. The client token is found in your app’s dashboard. Since the client token is used rarely, we won’t talk about it in this document. Instead it’s covered in any API documentation that uses the client token.

PHP Slim Middleware

[Fuente: http://docs.slimframework.com/#Middleware-Overview]

Middleware Overview

The Slim Framework implements a version of the Rack protocol. As a result, a Slim application can have middleware that may inspect, analyze, or modify the application environment, request, and response before and/or after the Slim application is invoked.

Middleware Architecture

Think of a Slim application as the core of an onion. Each layer of the onion is middleware. When you invoke the Slim application’s run() method, the outer-most middleware layer is invoked first. When ready, that middleware layer is responsible for optionally invoking the next middleware layer that it surrounds. This process steps deeper into the onion – through each middleware layer – until the core Slim application is invoked. This stepped process is possible because each middleware layer, and the Slim application itself, all implement a public call() method. When you add new middleware to a Slim application, the added middleware will become a new outer layer and surround the previous outer middleware layer (if available) or the Slim application itself.

Application Reference

The purpose of middleware is to inspect, analyze, or modify the application environment, request, and response before and/or after the Slim application is invoked. It is easy for each middleware to obtain references to the primary Slim application, its environment, its request, and its response:

<?php
class MyMiddleware extends \Slim\Middleware
{
    public function call()
    {
        //The Slim application
        $app = $this->app;

        //The Environment object
        $env = $app->environment;

        //The Request object
        $req = $app->request;

        //The Response object
        $res = $app->response;
    }
}

Changes made to the environment, request, and response objects will propagate immediately throughout the application and its other middleware layers. This is possible because every middleware layer is given a reference to the same Slim application object.

Next Middleware Reference

Each middleware layer also has a reference to the next inner middleware layer with $this->next. It is each middleware’s responsibility to optionally call the next middleware. Doing so will allow the Slim application to complete its full lifecycle. If a middleware layer chooses not to call the next inner middleware layer, further inner middleware and the Slim application itself will not be run, and the application response will be returned to the HTTP client as is.

<?php
class MyMiddleware extends \Slim\Middleware
{
    public function call()
    {
        //Optionally call the next middleware
        $this->next->call();
    }
}

How to Use Middleware

Use the Slim application’s add() instance method to add new middleware to a Slim application. New middleware will surround previously added middleware, or the Slim application itself if no middleware has yet been added.

Example Middleware

This example middleware will capitalize the Slim application’s HTTP response body.

<?php
class AllCapsMiddleware extends \Slim\Middleware
{
    public function call()
    {
        // Get reference to application
        $app = $this->app;

        // Run inner middleware and application
        $this->next->call();

        // Capitalize response body
        $res = $app->response;
        $body = $res->getBody();
        $res->setBody(strtoupper($body));
    }
}

Add Middleware

<?php
$app = new \Slim\Slim();
$app->add(new \AllCapsMiddleware());
$app->get('/foo', function () use ($app) {
    echo "Hello";
});
$app->run();

The Slim application’s add() method accepts one argument: a middleware instance. If the middleware instance requires special configuration, it may implement its own constructor so that it may be configured before it is added to the Slim application.

When the example Slim application above is run, the HTTP response body will be an enthusiastic “HELLO”;

How to Write Middleware

Slim application middleware must subclass \Slim\Middleware and implement a public call() method. The call() method does not accept arguments. Middleware may implement its own constructor, properties, and methods. I encourage you to look at Slim’s built-in middleware for working examples (e.g. Slim/Middleware/ContentTypes.php or Slim/Middleware/SessionCookie.php).

This example is the most simple implementation of Slim application middleware. It extends \Slim\Middleware, implements a public call() method, and calls the next inner middleware.

<?php
class MyMiddleware extends \Slim\Middleware
{
    public function call()
    {
        $this->next->call();
    }
}

GAE: Task Queue

[Fuente: https://developers.google.com/appengine/docs/java/taskqueue/]

With the Task Queue API, applications can perform work outside of a user request, initiated by a user request. If an app needs to execute some background work, it can use the Task Queue API to organize that work into small, discrete units, called tasks. The app adds tasks to task queues to be executed later.

App Engine provides two different queue configurations:

  1. Push queues process tasks based on the processing rate configured in the queue definition. App Engine automatically scales processing capacity to match your queue configuration and processing volume, and also deletes tasks after processing. Push queues are the default.
  2. Pull queues allow a task consumer (either your application or code external to your application) to lease tasks at a specific time for processing within a specific timeframe. Pull queues give you more control over when tasks are processed, and also allow you to integrate your application with non-App-Engine code using the experimental Task Queue REST API. When using pull queues, your application needs to handle scaling of instances based on processing volume, and also needs to delete tasks after processing.

This page provides the basic concepts common to both types of queues. Once you’ve understood the basics, you can check out the queue configuration pagepush queue overview, and pull queue overview to see how to configure and use these two types of queues.

  1. Task Queue concepts
  2. Task concepts
  3. Asynchronous operations

Task Queue concepts

Tasks queues are an efficient and powerful tool for background processing; they allow your application to define tasks, add them to a queue, and then use the queue to process them in aggregate. You name queues and configure their properties in a configuration file named queue.xml.

Push queues function only within the App Engine environment. These queues are the best choice for applications whose tasks work only with App Engine tools and services. With push queues, you simply configure a queue and add tasks to it. App Engine handles the rest. Push queues are easier to implement, but are restricted to use within App Engine. For more information about push queues and examples of how to use them, see Using Push Queues.

If you want to use a different system to consume tasks, pull queues are your best choice. In pull queues, a task consumer (either in your App Engine application, a backend, or code outside of App Engine) leases a specific number of tasks from a specific queue for a specific timeframe. After leasing tasks from a pull queue, the task consumer is responsible for deleting them. If you are consuming tasks from within App Engine, you can use calls from the com.google.appengine.api.taskqueue package. If you are consuming tasks from outside of App Engine, you need to use the Task Queue REST API. Pull queues give you more power and flexibility over when and where tasks are processed, but they require you to handle scaling of workers based on processing volume. Your task consumer also needs to delete tasks after processing.

In summary, push queues allow you to process tasks within App Engine at a steady rate and App Engine scales computing resources according to the number of tasks in your queue. Pull queues allow an alternate task consumer to process tasks at a specific time, either in or outside App Engine, but your application needs to scale workers based on processing volume, as well as delete tasks after processing.

You can read more about these two types of queues in the Using Push Queues and Using Pull Queues.

The default queue

For convenience, App Engine provides a default push queue for each application (there is no default pull queue). If you do not name a queue for a task, App Engine automatically inserts it into the default queue. You can use this queue immediately without any additional configuration. All modules and versions of the same application share the same default task queue.

The default queue is preconfigured with a throughput rate of 5 task invocations per second. If you want to change the preconfigured settings, simply define a queue named default in queue.xml. Code may always insert new tasks into the default queue, but if you wish to disable execution of these tasks, you may do so by clicking the Pause Queue button in the Task Queues tab of the Administration Console.

Named queues

While the default queue makes it easy to enqueue tasks with no configuration, you can also create custom queues by defining them in queue.xml. Custom queues allow you to more effectively handle task processing by grouping similar types of tasks. You can control the processing rate—and a number of other settings—based specifically on the type of task in each queue.

All versions of an application share the same named task queues.

For more information about configuring queues in queue.xml, please see Task Queue Configuration.

Task queues in the Administration Console

You can manage task queues for an application using the Task Queue tab of the Administration Console. The Task Queue tab lists all of the queues in the application. Clicking on a queue name brings up the Task Queue Details page where you can see all of the tasks scheduled to run in a queue and you can manually delete individual tasks or purge every task from a queue. This is useful if a task in a push queue cannot be completed successfully and is stuck waiting to be retried. You can also pause and resume a queue on this page.

You can view details of individual tasks by clicking the task name from the list of tasks on the Task Queue Details page. This page allows you to debug why a task did not run successfully. You can also see information about the previous run of the task as well as the task’s body.

Checking task queue statistics

You can view queue statistics in the Console to determine performance and status. However, you can also access queue statistics programmatically using theQueueStatistics class.

Task concepts

task is a unit of work to be performed by the application. Each task is an object of the TaskOptions class. Each Task object contains an endpoint (with a request handler for the task and an optional data payload that parameterizes the task). You can enqueue push tasks to a queue defined in queue.xml. Push tasks and pull tasks are defined differently; see the Using Push Queues and Using Pull Queues for specific usage details.

Task names

In addition to a task’s contents, you can declare a task’s name. Once a task with name N is written, any subsequent attempts to insert a task named N fail.

While this is generally true, task names do not provide an absolute guarantee of once-only semantics. In rare cases, multiple calls to create a task of the same name may succeed. It’s also possible in exceptional cases for a task to run more than once—even if it was only created once.

All project, queue, and task names must be a combination of one or more digits, letters a–z, underscores, and/or dashes, satisfying the following regular expression:

[0-9a-zA-Z\-\_]+

Task names may be up to 500 characters long.

If a push task is created successfully, it will eventually be deleted (at most seven days after the task successfully executes). Once deleted, its name can be reused.

If a pull task is created successfully, your application needs to delete the task after processing. The system may take up to seven days to recognize that a task has been deleted; during this time, the task name remains unavailable. Attempting to create another task during this time with the same name will result in an “item exists” error. The system offers no method to determine if deleted task names are still in the system. To avoid these issues, we recommend that you let App Engine generate the task name automatically.

Tasks within transactions

You can enqueue a task as part of a datastore transaction, such that the task is only enqueued—and guaranteed to be enqueued—if the transaction is committed successfully. Tasks added within a transaction are considered to be a part of it and have the same level of isolation and consistency.

An application cannot insert more than five transactional tasks into task queues during a single transaction. Transactional tasks must not have user-specified names.

The following code sample demonstrates how to insert transactional tasks into a push queue as part of a datastore transaction:

DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Queue queue = QueueFactory.getDefaultQueue();
try {
    Transaction txn = ds.beginTransaction();

    // ...

    queue.add(TaskOptions.Builder.withUrl("/path/to/my/worker"));

    // ...
    txn.commit();
} catch (DatastoreFailureException e) {
}

Deleting tasks

You have two ways to delete tasks:

  1. Using the Administration Console.
  2. Programmatically, using purge() to delete all tasks from the specified queue, or using deleteTask() to delete an individual task:
// Purge entire queue...
Queue queue = QueueFactory.getQueue("foo");
queue.purge();

// Delete an individual task...
Queue q = QueueFactory.getQueue("queue1");
q.deleteTask("foo")

Asynchronous operations

If you want to make asynchronous calls to a task queue, you use the asynchronous methods provided by the Queue class. Call get on the returned Future to force the request to complete. When asynchronously adding tasks in a transaction, you should call get() on the Future before committing the transaction to ensure that the request has finished.

 

Using Push Queues in Java

In App Engine push queues, a task is a unit of work to be performed by the application. Each task is an object of the TaskOptions class. Each Task object contains an application-specific URL with a request handler for the task, and an optional data payload that parameterizes the task.

For example, consider a calendaring application that needs to notify an invitee, via email, that an event has been updated. The data payload for this task consists of the email address and name of the invitee, along with a description of the event. The webhook might live at /app_worker/send_email and contain a function that adds the relevant strings to an email template and sends the email. The app can create a separate task for each email it needs to send.

You can use push queues only within the App Engine environment; if you need to access App Engine tasks from outside of App Engine, use pull queues.

  1. Using push queues
  2. Push task execution
  3. Deferred tasks
  4. URL endpoints
  5. Push queues and the development server
  6. Push queues and backends
  7. Quotas and limits for push queues

Using push queues

A Java app sets up queues using a configuration file named queue.xml, in the WEB-INF/ directory inside the WAR. See Java Task Queue Configuration. Every app has a push queue named default with some default settings.

To enqueue a task, you get a Queue using the QueueFactory, then call its add() method. You can get a named queue specified in the queue.xml file using thegetQueue() method of the factory, or you can get the default queue using getDefaultQueue(). You can call the Queue‘s add() method with a TaskOptions instance (produced by TaskOptions.Builder), or you can call it with no arguments to create a task with the default options for the queue.

The following code adds a task to a queue with options.

In index.html:

<!-- A basic index.html file served from the "/" URL. -->
<html>
  <body>
    <p>Enqueue a value, to be processed by a worker.</p>
    <form action="/enqueue" method="post">
      <input type="text" name="key">
      <input type="submit">
    </form>
  </body>
</html>

In Enqueue.java:

// The Enqueue servlet should be mapped to the "/enqueue" URL.
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.*;

public class Enqueue extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String key = request.getParameter("key");

        // Add the task to the default queue.
        Queue queue = QueueFactory.getDefaultQueue();
        queue.add(withUrl("/worker").param("key", key));

        response.sendRedirect("/");
    }
}

In Worker.java:

// The Worker servlet should be mapped to the "/worker" URL.
public class Worker extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String key = request.getParameter("key");
        // Do something with key.
    }
}

Tasks added to this queue will execute by calling the request handler at the URL /worker with the parameter key. They will execute at the rate set in thequeue.xml file, or the default rate of 5 tasks per second.

Push task execution

App Engine executes push tasks by sending HTTP requests to your app. Specifying a programmatic asynchronous callback as an HTTP request is sometimes called a web hook. The web hook model enables efficient parallel processing.

The task’s URL determines the handler for the task and the module that runs the handler.

The handler is determined by the path part of the URL (the forward-slash separated string following the hostname), which is specified by the url parameter in the TaskOptions that you include in your call to the Queue.add() method. The url must be relative and local to your application’s root directory.

 

The module (or frontend or backend) and version in which the handler runs is determined by:

 

If you do not specify any of these parameters, the task will run in the same module/version in which it was enqueued, subject to these rules:

  • If the default version of the app enqueues a task, the task will run on the default version. Note that if the app enqueues a task and the default version is changed before the task actually runs, the task will be executed in the new default version.
  • If a non-default version enqueues a task, the task will always run on that same version.

The namespace in which a push task runs is determined when the task is added to the queue. By default, a task will run in the current namespace of the process that created the task. You can override this behavior by explicitly setting the namespace before adding a task to a queue, as described on the multitenancy page.

A task must finish executing and send an HTTP response value between 200–299 within 10 minutes of the original request. This deadline is separate from user requests, which have a 60-second deadline. If your task’s execution nears the limit, App Engine raises a DeadlineExceededException that you can catch to save your work or log progress before the deadline passes. If the task failed to execute, App Engine retries it based on criteria that you can configure.

Task request headers

Requests from the Task Queue service contain the following HTTP headers:

  • X-AppEngine-QueueName, the name of the queue (possibly default)
  • X-AppEngine-TaskName, the name of the task, or a system-generated unique ID if no name was specified
  • X-AppEngine-TaskRetryCount, the number of times this task has been retried; for the first attempt, this value is 0. This number includes attempts where the task failed due to a lack of available instances and never reached the execution phase.
  • X-AppEngine-TaskExecutionCount, the number of times this task has previously failed during the execution phase. This number does not include failures due to a lack of available instances.
  • X-AppEngine-TaskETA, the target execution time of the task, specified in milliseconds since January 1st 1970.

These headers are set internally by Google App Engine. If your request handler finds any of these headers, it can trust that the request is a Task Queue request. If any of the above headers are present in an external user request to your app, they are stripped. The exception being requests from logged in administrators of the application, who are allowed to set the headers for testing purposes.

Tasks may be created with the X-AppEngine-FailFast header, which specifies that a task running on a backend fails immediately instead of waiting in a pending queue.

Google App Engine issues Task Queue requests from the IP address 0.1.0.2.

The rate of task execution

You set the maximum processing rate for the entire queue when you configure the queue. App Engine uses a token bucket algorithm to execute tasks once they’ve been delivered to the queue. Each queue has a token bucket, and each bucket holds a certain number of tokens. Your app consumes a token each time it executes a task. If the bucket runs out of tokens, the system pauses until the bucket has more tokens. The rate at which the bucket is refilled is the limiting factor that determines the rate of the queue. See Defining Push Queues and Processing Rates for more details.

To ensure that the Task Queue system does not overwhelm your application, it may throttle the rate at which requests are sent. This throttled rate is known as theenforced rate. The enforced rate may be decreased when your application returns a 503 HTTP response code, or if there are no instances able to execute a request for an extended period of time. You can view the enforced rate on the Task Queue tab of the Administration Console.

The order of task execution

The order in which tasks are executed depends on several factors:

  • The position of the task in the queue. App Engine attempts to process tasks based on FIFO (first in, first out) order. In general, tasks are inserted into the end of a queue, and executed from the head of the queue.
  • The backlog of tasks in the queue. The system attempts to deliver the lowest latency possible for any given task via specially optimized notifications to the scheduler. Thus, in the case that a queue has a large backlog of tasks, the system’s scheduling may “jump” new tasks to the head of the queue.
  • The value of the task’s etaMillis property. This specifies the earliest time that a task can execute. App Engine always waits until after the specified ETA to process push tasks.
  • The value of the task’s countdownMillis property. This specifies the minimum number of seconds to wait before executing a task. Countdown and eta are mutually exclusive; if you specify one, do not specify the other.

Task retries

If a push task request handler returns an HTTP status code within the range 200–299, App Engine considers the task to have completed successfully. If the task returns a status code outside of this range, App Engine retries the task until it succeeds. The system backs off gradually to avoid flooding your application with too many requests, but schedules retry attempts for failed tasks to recur at a maximum of once per hour.

You can also configure your own scheme for task retries using the retry-parameters element in queue.xml.

When implementing the code for tasks (as worker URLs within your app), it is important to consider whether the task is idempotent. App Engine’s Task Queue API is designed to only invoke a given task once; however, it is possible in exceptional circumstances that a task may execute multiple times (such as in the unlikely case of major system failure). Thus, your code must ensure that there are no harmful side-effects of repeated execution.

Deferred tasks

Setting up a handler for each distinct task (as described in the previous sections) can be cumbersome, as can serializing and deserializing complex arguments for the task — particularly if you have many diverse but small tasks that you want to run on the queue. The Java SDK includes an interface called DeferredTask. This interface lets you define a task as a single method. This interface uses Java serialization to package a unit of work into a Task Queue. A simple return from that method is considered success. Throwing any exception from that method is considered a failure.

URL endpoints

Push tasks reference their implementation via URL. For example, a task which fetches and parses an RSS feed might use a worker URL called/app_worker/fetch_feed. You can specify this worker URL or use the default. In general, you can use any URL as the worker for a task, so long as it is within your application; all task worker URLs must be specified as relative URLs:

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions.Method;
import com.google.appengine.api.taskqueue.TaskOptions.Builder.*;

// ...
    Queue queue = QueueFactory.getDefaultQueue();
    queue.add(withUrl("/path/to/my/worker"));
    queue.add(withUrl("/path?a=b&c=d").method(Method.GET));

If you do not specify a worker URL, the task uses a default worker URL named after the queue:

/_ah/queue/queue_name

A queue’s default URL is used if, and only if, a task does not have a worker URL of its own. If a task does have its own worker URL, then it is only invoked at the worker URL, never another. Once inserted into a queue, its url endpoint cannot be changed.

You can also target tasks to App Engine Backends. Backends allow you to process tasks beyond the 10-minute deadline for task execution. See Push Queues and Backends for more information.

Securing URLs for tasks

If a task performs sensitive operations (such as modifying important data), you might want to secure its worker URL to prevent a malicious external user from calling it directly. You can prevent users from accessing URLs of tasks by restricting access to administrator accounts. Task queues can access admin-only URLs. You can read about restricting URLs at Security and Authentication. An example you would use in web.xml to restrict everything starting with /tasks/ to admin-only is:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>tasks</web-resource-name>
        <url-pattern>/tasks/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

For more on the format of web.xml, see the documentation on the deployment descriptor.

To test a task web hook, sign in as an administrator and visit the URL of the handler in your browser.

Push queues and the development server

When your app is running in the development server, tasks are automatically executed at the appropriate time just as in production.

To disable automatic execution of tasks, set the following jvm flag:

--jvm_flag=-Dtask_queue.disable_auto_task_execution=true

You can examine and manipulate tasks from the developer console at: http://localhost:8000/_ah/admin/taskqueue.

To execute tasks, select the queue by clicking on its name, select the tasks to execute, and click Run Now. To clear a queue without executing any tasks, clickPurge Queue.

The development server and the production server behave differently:

  • The development server doesn’t respect the <rate> and <bucket-size> attributes of your queues. As a result, tasks are executed as close to their ETA as possible. Setting a rate of 0 doesn’t prevent tasks from being executed automatically.
  • The development server doesn’t retry tasks.
  • The development server doesn’t preserve queue state across server restarts.

Push queues and backends

Push tasks typically must finish execution within 10 minutes. If you have push tasks that require more time or computing resources to process, you can use App Engine Backends to process these tasks outside of the normal limits of App Engine applications. The following code sample demonstrates how to create a push task addressed to an instance 1 of a backend named backend1:

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.*;
import com.google.appengine.api.backends.*;

// ...
    queue.add(withUrl("/path/to/my/worker").param("key", key).header("Host",
    BackendServiceFactory.getBackendService().getBackendAddress("backend1", 1));

Quotas and limits for push queues

Enqueuing a task in a push queue counts toward the following quotas:

  • Task Queue Stored Task Count
  • Task Queue Stored Task Bytes
  • Task Queue API Calls

The Task Queue Stored Task Bytes quota is configurable in queue.xml by setting <total-storage-limit>. This quota counts towards your Stored Data (billable) quota.

Execution of a task counts toward the following quotas:

  • Requests
  • Incoming Bandwidth
  • Outgoing Bandwidth

The act of executing a task consumes bandwidth-related quotas for the request and response data, just as if the request handler were called by a remote client. When the task queue processes a task, the response data is discarded.

Once a task has been executed or deleted, the storage used by that task is reclaimed. The reclaiming of storage quota for tasks happens at regular intervals, and this may not be reflected in the storage quota immediately after the task is deleted.

For more information on quotas, see Quotas, and the “Quota Details” section of the Admin Console.

The following limits apply to the use of push queues:

Push Queue Limits
Maximum task size 100KB
Maximum number of active queues (not including the default queue) Free apps: 10 queues, Billed apps: 100 queues
Queue execution rate 500 task invocations per second per queue
Maximum countdown/ETA for a task 30 days from the current date and time
Maximum number of tasks that can be added in a batch 100 tasks
Maximum number of tasks that can be added in a transaction 5 tasks

Iris: Modern front-end development: Iris + GruntJS + NodeJS

[Fuente: http://www.thegameofcode.com/2013/05/front-end-with-iris-nodejs-gruntjs.html]

¿Por qué utilizar Iris?

Los motores de Javascript actuales están evolucionando rápidamente y se están volviendo más rápidos y potentes.

Sin embargo por otro lado es común encontrar códigos fuente de Javascript realmente feos y liosos. El framework de Iris te guía en la buena dirección y te ayuda a programar aplicaciones web complejas:

  • Buena organización del código fuente: All our JS code is split into small files providing scalability & maintainability. Don’t worry about the large number of files created because you can concatenate them using grunt, thus avoiding a large number of HTTP requests.
  • Solución single-page: Navigation is performed without reloading the page using hash-URLs, e.g. gmail. The navigation system is resolved in a simple way using JS, therefore performance is improved.
  • Motor rápido de templates: With Iris templates you can easily add multilanguage texts into screens and UIs using @@ annotations. In the same way you can print formatted strings as currencies, dates, numbers, etc…
  • Muy ligero de peso con lo que se carga rápido: Iris is very light, about 16KB,  and only depends on jQuery. All files are HTML or JS, therefore your front-end code is independent of server technology. This allows Iris components to be cached by the browser.
  • Buena documentación: 

About Iris

Iris te proporciona la estructura las aplicaciones web proporcionando templates , componentes UI , screens and resources.

Todos los paths de los componentes deben ser definidos previamente en  iris.path  ,por ejemplo.: iris.path.ui = “path/to/my/ui.js”

    • Templates are written in HTML files. These are stored in separate *.html files, therefore a layout designer can edit them easily.
<div> <!-- All templates must have a root element -->

    <!-- The (@@...@@) annotations will print translated text -->
    <b>@@name@@</b>:

    <!-- The (##...##) annotations will be replaced by template params -->
    <b>##user.name##</b>

    <!-- The button element can be used by a UI or Screen due the data-id attr -->
    <button data-id="accept_button">@@ok@@</button>
</div>
    • UIs are graphical components written in JS that can be reused. UIs uses a template to add functionality and manage events. These objects are stored in separate *.js files.
iris.ui(

    // "self" is equal to "this" but can be used
    // inside private functions
    function(self) {

        // function called automatically once when a UI
        // of this kind is instanced
        self.create = function() {
            // set the template
            self.tmpl(iris.path.ui.html);

            // return the jq element with data-id=button
            self.get("button").on("click", function (){
                alert( "Hi " + self.setting("name") + "!");
            });
        };

    }
);
      You can use UIs to create an HTML element into a template:

 

<!-- The "name" setting will be "Jonh" -->
<div data-id="my_ui" data-name="Jonh"></div>
      The <div> element will be replaced by the template

template.html

      :
<div>
    <b>UI Example</b>
    <button data-id="button">Say Hi!</button>
</div>
      Finally, you can create a UI instance inside screens or other UIs:

 

self.ui("my_ui", iris.path.ui.js);
      The result:

 

UI Example 
    • Screens are navigable elements, bound to a unique hash fragment (#screen), and are stored in separate *.js files. Screens use a template to add functionality and manage events like UI does. You can also instantiate UI objects within screens. Screens’s declaration are very similar to the UI’s declaration, use iris.screen() instead ofiris.ui()
iris.screen(

    // "self" is equal to "this" but can be used
    // inside private functions
    function(self) {

        // function called automatically once when this screen is instanced
        self.create = function() {
            // set the template
            self.tmpl(iris.path.screen.html);

            // created a UI component, the setting "name" will be "Anonymous"
            self.ui("my_ui", iris.path.ui.js, { name : "Anonymous" });
        };
    }
);
      The content of the screen will be

iris.path.screen.html

      .

 

<div>
    <b>My Screen</b><br />
    <div data-id="my_ui"></div>
</div>
      Finally, you can create this screen navigating to them, use

iris.navigate()

       or type the URL into your browser:
iris.navigate("#/my_screen");

 

      The result is a web page like:

 

My Screen
UI Example 

Grunt advantages

The most important advantage of Grunt is automation. With hundreds of plugins to choose from, you can do repetitive tasks like minification, compilation, unit testing, linting, etc. After you’ve configured it, a task runner can do most of that mundane work for you with a minimum of effort.

To run Grunt you need to install NodeJS, for more information please visit http://gruntjs.com/getting-started.

Complete example

I have written a complete example based on the famous todomvc, using Iris and Grunt.
If you want to learn more about the iris-todomvc please check out this tutorial.

Project structure

Gruntfile.js
All of the configuration for our Grunt build process sits inside our Gruntfile (Gruntfile.js) in our project directory.

module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    jshint: {
      all: ['Gruntfile.js', 'src/app/**/*.js']
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: '<%= pkg.iris.baseUri %>**/*.js',
        dest: '<%= pkg.iris.init %>'
      }
    },
    connect: {
      dev: {
        options: {
          base: 'src'
        }
      },
      dist: {
        options: {
          base: 'dist'
        }
      }
    },
    watch: {
      scripts: {
        files: ['src/**/*.js'],
        tasks: ['jshint'],
        options: {
          nospawn: true
        }
      }
    },
    copy: {
      main: {
        files: [
          {expand: true, cwd: 'src/', src: ['**'], dest: 'dist/'}
        ]
      }
    },
    clean: ['dist']
  });

  // Custom task to concatenate Iris templates
  grunt.registerTask('iris-tmpl', function() {
    grunt.log.writeln('Concatenate Iris templates...');

    var pkg = grunt.file.readJSON('package.json');
    var irisBaseUri = pkg.iris.baseUri;
    var destFile = pkg.iris.init;
    grunt.log.writeln('Configurations: baseUri['+ irisBaseUri +'] init['+ destFile +']');

    var templates = '';
    grunt.file.expand({cwd : irisBaseUri}, '**/*.html').forEach(function(file){
        var source = grunt.file.read(irisBaseUri + file, {encoding : "utf8"})
                .replace(/[\r\n\t]/g, '').replace(/\"/g, '\\"');
        templates += 'iris.tmpl("' + file + '", "' + source + '");\n';
    });

    var irisTmpl = grunt.file.read(destFile, {encoding : "utf8"}) + templates;
    grunt.file.write(destFile, irisTmpl);
    grunt.log.writeln('File "' + destFile + '" saved.');
  });

  // Load tasks
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-clean');

  // Register Grunt tasks
  grunt.registerTask('default', ['jshint', 'clean', 'copy', 'uglify', 'iris-tmpl']);
  grunt.registerTask('devserver', ['jshint', 'connect:dev', 'watch']);
  grunt.registerTask('distserver', ['default', 'connect:dist', 'watch']);

};

package.json
To run this example, first install Nodejs and Grunt, after go to project’s root folder and execute $ npm install then you can run the Grunt tasks.

{
  "name": "todomvc-iris",
  "description": "TodoMVC - Iris",
  "version": "1.0.0",
  "private": true,
  "iris" : {
    "init" : "dist/app/init.js",
    "baseUri" : "src/app/"
  },
  "devDependencies": {
    "grunt": "0.4.1",
    "grunt-contrib-uglify": "0.2.0",
    "grunt-contrib-connect": "0.3.0",
    "grunt-contrib-watch": "0.3.1",
    "grunt-contrib-jshint": "0.4.3",
    "grunt-contrib-concat": "0.2.0",
    "grunt-contrib-copy": "0.4.1",
    "grunt-contrib-clean": "0.4.1"
  }
}

Grunt tasks

  • $ grunt Use this command to build the application.
    $ grunt
    Running "jshint:all" (jshint) task
    >> 5 files lint free.
    
    Running "clean:0" (clean) task
    Cleaning "dist"...OK
    
    Running "copy:main" (copy) task
    Created 4 directories, copied 12 files
    
    Running "uglify:build" (uglify) task
    File "dist/app/init.js" created.
    
    Running "iris-tmpl" task
    Concatenate Iris templates...
    Configurations: baseUri[src/app/] init[dist/app/init.js]
    File "dist/app/init.js" saved.
    
    Done, without errors.

    How it works?

    1. First, Grunt checks code style and validates it using JSHint, if all goes well, it will copy all files from /src to/dist folder.
    2. The next step is to concatenate and compress the source code of our application (src/app/**.js files).
    3. Thereafter, all iris components (screens, UIs & resources) have been concatenated into a single file (dist/app/init.js).
      ...
      uglify: {
            ...
            build: {
              src: '<%= pkg.iris.baseUri %>**/*.js',
              dest: '<%= pkg.iris.init %>'
            }
          },
      ...
    4. Finally, all Iris templates are concatenated using the custom task iris-tmpl. All HTML have been appended to dist/app/init.js.

    You can change this packaging settings in package.json:

    ...
     "iris" : {
        "init" : "dist/app/init.js",
        "baseUri" : "src/app/"
      }
    ...

    All application files are a single file of 5.33 kb. This is perfect for slow connections like 3G networks.

  • $ grunt devserver Use this command to test and develop the web application.
    $ grunt devserver
    Running "jshint:all" (jshint) task
    >> 5 files lint free.
    
    Running "connect:dev" (connect) task
    Started connect web server on localhost:8000.
    
    Running "watch" task
    Waiting...
    1. This command checks code style and validates it using JSHint
    2. It starts a local web server and sets its root folder to /src and waits for file changes. You can test the web in your browser and, at the same time, change the files with some text editor.
  • $ grunt distserver Execute this command to test the build
    $ grunt distserver
    Running "jshint:all" (jshint) task
    >> 5 files lint free.
    
    Running "clean:0" (clean) task
    Cleaning "dist"...OK
    
    Running "copy:main" (copy) task
    Created 4 directories, copied 12 files
    
    Running "uglify:build" (uglify) task
    File "dist/app/init.js" created.
    
    Running "iris-tmpl" task
    Concatenate Iris templates...
    Configurations: baseUri[src/app/] init[dist/app/init.js]
    File "dist/app/init.js" saved.
    
    Running "connect:dist" (connect) task
    Started connect web server on localhost:8000.
    
    Running "watch" task
    Waiting...
    1. Grunt executes the default task (like $ grunt)
    2. It initializes a local server to test the generated website at /dist folder. You can test the final application typing into your browser: http://localhost:8000/

Download the Iris todo-list example and learn how to create your own Iris + Grunt + Nodejs applications.
You can find the demo here.

Enjoy it!

Autenticación en Single Page Applications With Angular.js

[Fuente: http://www.frederiknakstad.com/2013/01/21/authentication-in-single-page-applications-with-angular-js/]

I have been working a lot with Angular.js lately, and love how easy it makes it to create web applications with rich client-side functionality. It’s an extremely useful asset in keeping your own client-side code lean, making it easy to separate business logic and declarative markup for anything view specific. However, it’s not all roses, and I’m still struggling to find the best solutions to some problems I have encountered. One of which is a problem that exceeds the scope of Angular…

How do I deal with authentication and authorization in single page applications?

Since only the initial load of the app is a full page load, and subsequent communication with the server is entirely done via XHRs I need a slightly different model from the traditional one. One option I tried out was to have a traditional login page which, on success, redirects to a secured URL which loads the actual application. This has the added benefit that the client side code and view templates used for pages intended for logged in users are not accessible to anyone not logged in. However, in many cases this is not much of a concern as long as the communication against the resource API is properly secured. Preventing anyone from getting or modifying sensitive data is a server-side issue, and should therefore be properly handled there. I wanted a seamless user experience with no full page reloads beyond the initial page load, so I decided to play around a little and see if I could come up with an alternative. These are the core characteristics of the solution I set out to implement:

  1. The server needs to communicate what permissions/role the client has on inital page load.
  2. The client app then needs to keep track of the user’s login status, and change it accordingly when the user logs in or out. These operations should not cause a full page reload.
  3. The access level of the routes should be declared as part of the rest of the routing configuration.

Configuring access levels and user roles

To indicate the available user roles and access levels to be used in the routing system, I decided to make a separate module which could be used both on the client and server side (with Node.js):

(function(exports){

    var userRoles = {
        public: 1, // 001
        user:   2, // 010
        admin:  4  // 100
    };

    exports.userRoles = userRoles;
    exports.accessLevels = {
        public: userRoles.public | // 111
                userRoles.user   | 
                userRoles.admin,   
        anon:   userRoles.public,  // 001
        user:   userRoles.user |   // 110
                userRoles.admin,                    
        admin:  userRoles.admin    // 100
    };

})(typeof exports === 'undefined'? this['routingConfig']={}: exports);

Both the user roles and access levels are defined as numbers so that it is possible to calculate permissions using binary operations. User roles are defined by which bit is set to 1, while access levels indicate whether that user role has access by setting the corresponding bit to either 1 or 0 in the bitmask1. So, here is an example of binary operations that can be calculated to see whether the user role user has access to the access levelsadmin and public:

    010 // = userRole: user
AND 100 // = accessLevel admin  
=   000 // = false, no access

    010 // = userRole: user
AND 111 // = accessLevel public  
=   010 // = true, go ahead

Setting up client-side routing

When I define my routes I want to indicate what access level each route should have, so I add a new property to each route, called access, like so:

angular.module('myApp', ['myApp.services', 'ngCookies'])  
.config(['$routeProvider', '$locationProvider',
    function ($routeProvider, $locationProvider) {

    // ...

    var access = routingConfig.accessLevels;

    $routeProvider.when('/register',
        {
            templateUrl:    'partials/register',
            controller:     RegisterCtrl,
            access:         access.anon
        });
    $routeProvider.when('/private',
        {
            templateUrl:    'partials/private',
            controller:     PrivateCtrl,
            access:         access.user
        });
    $routeProvider.when('/admin',
        {
            templateUrl:    'partials/admin',
            controller:     AdminCtrl,
            access:         access.admin
        });

    // ...

}]);

Communicating login status to client side app on initial page load

When the user first loads the client side app, whether he’s trying to access a restricted or public route initially, the server needs to communicate the current role of the user. Since the client side app can’t decrypt the authentication cookie set by the server, I decided to communicate login status via the HTTP response which serves up the single page application. I decided on doing this by setting a cookie, which the client would clear upon reading it. I feel like there’s something icky about doing it this way, so I’m open to any alternative solutions here. Anyway, the server sets the cookie like so:

app.get('/*', function(req, res){  
    var role = userRoles.public, username = '';
    if(req.user) {
        role = req.user.role;
        username = req.user.username;
    }

    res.cookie('user', JSON.stringify({
        'username': username,
        'role': role
    }));

    res.render('index');
});

On the client-side I have an Angular service, Auth, which upon initialization will read in this cookie, save the login status, then discard the cookie. This service will also make the access levels, user roles, and current user available to the rest of the application.

angular.module('angular-client-side-auth')  
.factory('Auth', function($http, $rootScope, $cookieStore){

    var accessLevels = routingConfig.accessLevels
        , userRoles = routingConfig.userRoles
        , currentUser = $cookieStore.get('user') || 
                        { username: '', role: userRoles.public };

    // ...

    return {

        // ...

        accessLevels: accessLevels,
        userRoles: userRoles,
        user: currentUser
    };

});

So, now my Angular app knows what login status the user has, and can perform the client side routing based on this status.

Enforcing the routing policy client-side

Warning: I want to stress the importance of securing your server-side API once-again. The routing policy we’re “enforcing” client-side isvery easy to get around using Chrome Developer Tools or Firebug. The technique I’m describing is used as a way of tailoring your views and giving a better user experience, but malicious users can still change their user role and get access to client-side routes that should be restricted to them. This is not a problem as long as any sensitive data is accessed via your server-side API, and the proper authentication/authorization strategy is implemented there.

Now that I have access to the current user’s role and the access level of each route I can actually enforce the policy I’ve configured. So now I can add functionality like logging in, registering new users, authorizing a route, etc. to the Auth service I mentioned earlier.

angular.module('angular-client-side-auth')  
.factory('Auth', function($http, $rootScope, $cookieStore){

    // ...

    $rootScope.accessLevels = accessLevels;
    $rootScope.userRoles = userRoles;

    return {
        authorize: function(accessLevel, role) {
            if(role === undefined)
                role = $rootScope.user.role;
            return accessLevel &amp; role;
        },

        isLoggedIn: function(user) {
            if(user === undefined)
                user = $rootScope.user;
            return user.role === userRoles.user || user.role === userRoles.admin;
        },

        register: function(user, success, error) {
            $http.post('/register', user).success(success).error(error);
        },

        login: function(user, success, error) {
            $http.post('/login', user).success(function(user){
                $rootScope.user = user;
                success(user);
            }).error(error);
        },

        logout: function(success, error) {
            $http.post('/logout').success(function(){
                $rootScope.user = {
                    username = '',
                    role = userRoles.public
                };
                success();
            }).error(error);
        },

        accessLevels: accessLevels,
        userRoles: userRoles
    };
});

This service can easily be reused by injecting it into any other component of my Angular application. For example, to actually enforce my routing policy I need to utilize the authorize() and isLoggedIn() functions in an event handler on $routeChangeStart:

angular.module('angular-client-side-auth', ['ngCookies'])  
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {

    $rootScope.$on("$routeChangeStart", function (event, next, current) {
        if (!Auth.authorize(next.access)) {
            if(Auth.isLoggedIn()) $location.path('/');
            else                  $location.path('/login');
        }
    });

}]);

Now, whenever a route is accessed, the proper authorization check will be performed before serving up the view, and a redirect will happen in case the user has insufficient permissions.

One gotcha here is that the session could time out making the client believe the user is logged in when in fact he is declined when communicating with the server resource API. However this can be rectified by using an HTTP interceptor to detect API calls that were denied. Mine is pretty simple, and just redirects to the login page in case of a 401.

angular.module('angularAuth', ['ngCookies'])  
.config([
    '$routeProvider', 
    '$locationProvider', 
    '$httpProvider', 
    function ($routeProvider, $locationProvider, $httpProvider) {

    // ...

    var interceptor = ['$location', '$q', function($location, $q) {
        function success(response) {
            return response;
        }

        function error(response) {

            if(response.status === 401) {
                $location.path('/login');
                return $q.reject(response);
            }
            else {
                return $q.reject(response);
            }
        }

        return function(promise) {
            return promise.then(success, error);
        }
    }];

    $httpProvider.responseInterceptors.push(interceptor);
});

Customizing views based on user role

I also want to introduce a directive I’ve made which you can use to show/hide elements based on the current user’s role. Here’s what it looks like:

angular.module('angular-client-side-auth')  
.directive('accessLevel', ['$rootScope', 'Auth', function($rootScope, Auth) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var prevDisp = element.css('display');
            $rootScope.$watch('user.role', function(role) {
                if(!Auth.authorize(attrs.accessLevel))
                    element.css('display', 'none');
                else
                    element.css('display', prevDisp);
            });
        }
    };
}]);

So, whenever you decorate a tag with the access-level directive, it will check the role of the current user, perform an authorization check using the injected Auth, and then show/hide the element. It also remembers the previous display property of the element in case the user logs out, thus changing her role.

I really want to stress again the point that this scheme exposes all the routing logic and view templates to the client, and can easily be manipulated by the end user. So, you still have to make sure that the calls to the server are properly authenticated and authorized there.

I have made a complete example with all the code available in this GitHub repository, which might illustrate my approach better than I have been able to in this blogpost.


  1. The routingConfig module in the GitHub repo will now automatically generate the required bit masks for you!

Javascript : Utilizando CORS (Cross-Origin-Resource-Sharing)

[Fuente: http://www.html5rocks.com/en/tutorials/cors/]

Introduction

APIs are the threads that let you stitch together a rich web experience. But this experience has a hard time translating to the browser, where the options for cross-domain requests are limited to techniques like JSON-P (which has limited use due to security concerns) or setting up a custom proxy (which can be a pain to set up and maintain).

Cross-Origin Resource Sharing (CORS) is a W3C spec that allows cross-domain communication from the browser. By building on top of the XMLHttpRequest object, CORS allows developers to work with the same idioms as same-domain requests.

The use-case for CORS is simple. Imagine the site alice.com has some data that the site bob.com wants to access. This type of request traditionally wouldn’t be allowed under the browser’s same origin policy. However, by supporting CORS requests, alice.com can add a few special response headers that allows bob.com to access the data.

As you can see from this example, CORS support requires coordination between both the server and client. Luckily, if you are a client-side developer you are shielded from most of these details. The rest of this article shows how clients can make cross-origin requests, and how servers can configure themselves to support CORS.

Making a CORS Request

This section shows how to make a cross-domain request in JavaScript.

Creating the XMLHttpRequest object

CORS is supported in the following browsers:

  • Chrome 3+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Internet Explorer 8+

(see the complete list of supported browsers at http://caniuse.com/#search=cors)

Chrome, Firefox, Opera and Safari all use the XMLHttpRequest2 object. Internet Explorer uses the similar XDomainRequest object, which works in much the same way as its XMLHttpRequest counterpart, but adds additional security precautions.

To get started, you will first need to create the appropriate request object. Nicholas Zakas wrote a simple helper method to help sort out the browser differences:

function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {

    // Check if the XMLHttpRequest object has a "withCredentials" property.
    // "withCredentials" only exists on XMLHTTPRequest2 objects.
    xhr.open(method, url, true);

  } else if (typeof XDomainRequest != "undefined") {

    // Otherwise, check if XDomainRequest.
    // XDomainRequest only exists in IE, and is IE's way of making CORS requests.
    xhr = new XDomainRequest();
    xhr.open(method, url);

  } else {

    // Otherwise, CORS is not supported by the browser.
    xhr = null;

  }
  return xhr;
}

var xhr = createCORSRequest('GET', url);
if (!xhr) {
  throw new Error('CORS not supported');
}

Event handlers

The original XMLHttpRequest object had only one event handler,onreadystatechange, which handled all responses. Although onreadystatechange is still available, XMLHttpRequest2 introduces a bunch of new event handlers. Here is a complete list:

Event Handler Description
onloadstart* When the request starts.
onprogress While loading and sending data.
onabort* When the request has been aborted. For instance, by invoking the abort() method.
onerror When the request has failed.
onload When the request has successfully completed.
ontimeout When the author specified timeout has passed before the request could complete.
onloadend* When the request has completed (either in success or failure).

* starred items are not supported by IE’s XDomainRequest
source: http://www.w3.org/TR/XMLHttpRequest2/#events

For most cases, you will at the very least want to handle the onload and onerror events:

xhr.onload = function() {
 var responseText = xhr.responseText;
 console.log(responseText);
 // process the response.
};

xhr.onerror = function() {
  console.log('There was an error!');
};

Browers don’t do a good job of reporting what went wrong when there is an error. For example, Firefox reports a status of 0 and an empty statusText for all errors. Browsers also report an error message to the console log, but this message cannot be accessed from JavaScript. When handling onerror, you will know that an error occurred, but not much else.

withCredentials

Standard CORS requests do not send or set any cookies by default. In order to include cookies as part of the request, you need to set the XMLHttpRequest’s.withCredentials property to true:

xhr.withCredentials = true;

In order for this to work, the server must also enable credentials by setting the Access-Control-Allow-Credentials response header to “true”. See the server section for details.

Access-Control-Allow-Credentials: true

The .withCredentials property will include any cookies from the remote domain in the request, and it will also set any cookies from the remote domain. Note that these cookies still honor same-origin policies, so your JavaScript code can’t access the cookies from document.cookie or the response headers. They can only be controlled by the remote domain.

Making the request

Now that your CORS request is configured, you are ready to make the request. This is done by calling the send() method:

xhr.send();

If the request has a body, it can be specified as an argument to send().

And thats it! Assuming the server is properly configured to respond to CORS requests, your onload handler will fire with the response, just like the standard same-domain XHR you are so familiar with.

End-to-End Example

Here is a full working sample of a CORS request. Run the sample and watch the network requests in the browser’s debugger to see the actual request being made.

Run Sample

// Create the XHR object.
function createCORSRequest(method, url) {
  var xhr = new XMLHttpRequest();
  if ("withCredentials" in xhr) {
    // XHR for Chrome/Firefox/Opera/Safari.
    xhr.open(method, url, true);
  } else if (typeof XDomainRequest != "undefined") {
    // XDomainRequest for IE.
    xhr = new XDomainRequest();
    xhr.open(method, url);
  } else {
    // CORS not supported.
    xhr = null;
  }
  return xhr;
}

// Helper method to parse the title tag from the response.
function getTitle(text) {
  return text.match('<title>(.*)?</title>')[1];
}

// Make the actual CORS request.
function makeCorsRequest() {
  // All HTML5 Rocks properties support CORS.
  var url = 'http://updates.html5rocks.com';

  var xhr = createCORSRequest('GET', url);
  if (!xhr) {
    alert('CORS not supported');
    return;
  }

  // Response handlers.
  xhr.onload = function() {
    var text = xhr.responseText;
    var title = getTitle(text);
    alert('Response from CORS request to ' + url + ': ' + title);
  };

  xhr.onerror = function() {
    alert('Woops, there was an error making the request.');
  };

  xhr.send();
}

Adding CORS support to the server

Most of the heavy lifting for CORS is handled between the browser and the server. The browser adds some additional headers, and sometimes makes additional requests, during a CORS request on behalf of the client. These additions are hidden from the client (but can be discovered using a packet analyzer such asWireshark).

CORS flow

Browser manufacturers are responsible for the browser-side implementation. This section explains how a server can configure its headers to support CORS.

Types of CORS requests

Cross-origin requests come in two flavors:

  1. simple requests
  2. “not-so-simple requests” (a term I just made up)

Simple requests are requests that meet the following criteria:

  • HTTP Method matches (case-sensitive) one of:
    • HEAD
    • GET
    • POST
  • HTTP Headers matches (case-insensitive):
    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type, but only if the value is one of:
      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain

Simple requests are characterized as such because they can already be made from a browser without using CORS. For example, a JSON-P request can issue a cross-domain GET request. Or HTML could be used to do a form POST.

Any request that does not meet the criteria above is a not-so-simple request, and requires a little extra communication between the browser and the server (called a preflight request), which we’ll get into below.

Handling a simple request

Lets start by examining a simple request from the client. The table below shows the JavaScript code for a simple GET request on the left, along with the actual HTTP request that the browser emits; CORS specific headers are in bold.

JavaScript:

var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('GET', url);
xhr.send();

HTTP Request:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

The first thing to note is that a valid CORS request *always* contains an Origin header. This Origin header is added by the browser, and can not be controlled by the user. The value of this header is the scheme (e.g. http), domain (e.g. bob.com) and port (included only if it is not a default port, e.g. 81) from which the request originates; for example: http://api.alice.com.

The presence of the Origin header does not necessarily mean that the request is a cross-origin request. While all cross-origin requests will contain an Origin header, some same-origin requests might have one as well. For example, Firefox doesn’t include an Origin header on same-origin requests. But Chrome and Safari include an Origin header on same-origin POST/PUT/DELETE requests (same-origin GET requests will not have an Origin header). Here is an example of a same-origin request with an Origin header:

HTTP Request:

POST /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.bob.com

The good news is that browsers don’t expect CORS response headers on same-origin requests. The response to a same-origin request is sent to user, regardless of whether it has CORS headers or not. However, if your server code returns an error if the Origin doesn’t match a list of allowed domains, be sure to include the origin the request comes from.

Here’s a valid server response; the CORS-specific headers are bolded

HTTP Response:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

All CORS related headers are prefixed with “Access-Control-“. Here’s some more details about each header.

Access-Control-Allow-Origin (required) – This header must be included in all valid CORS responses; omitting the header will cause the CORS request to fail. The value of the header can either echo the Origin request header (as in the example above), or be a ‘*’ to allow requests from any origin. If you’d like any site to be able to access your data, using ‘*’ is fine. But if you’d like finer control over who can access your data, use an actual value in the header.

Access-Control-Allow-Credentials (optional) – By default, cookies are not included in CORS requests. Use this header to indicate that cookies should be included in CORS requests. The only valid value for this header is true (all lowercase). If you don’t need cookies, don’t include this header (rather than setting its value to false).

The Access-Control-Allow-Credentials header works in conjunction with thewithCredentials property on the XMLHttpRequest 2 object. Both these properties must be set to true in order for the CORS request to succeed. If .withCredentials is true, but there is no Access-Control-Allow-Credentials header, the request will fail (and vice versa).

Its recommended that you don’t set this header unless you are sure you want cookies to be included in CORS requests.

Access-Control-Expose-Headers (optional) – The XMLHttpRequest 2 object has a getResponseHeader() method that returns the value of a particular response header. During a CORS request, the getResponseHeader() method can only access simple response headers. Simple response headers are defined as follows:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

If you want clients to be able to access other headers, you have to use theAccess-Control-Expose-Headers header. The value of this header is a comma-delimited list of response headers you want to expose to the client.

Handling a not-so-simple request

So that takes care of a simple GET request, but what if you want to do something more? Maybe you want to support other HTTP verbs like PUT or DELETE, or you want to support JSON using Content-Type: application/json. Then you need to handle what we’re calling a not-so-simple request.

A not-so-simple request looks like a single request to the client, but it actually consists of two requests under the hood. The browser first issues a preflight request, which is like asking the server for permission to make the actual request. Once permissions have been granted, the browser makes the actual request. The browser handles the details of these two requests transparently. The preflight response can also be cached so that it is not issued on every request.

Here’s an example of a not-so-simple request:

JavaScript:

var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader(
    'X-Custom-Header', 'value');
xhr.send();

Preflight Request:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Like the simple request, the browser adds the Origin header to every request, including the preflight. The preflight request is made as an HTTP OPTIONS request (so be sure your server is able to respond to this method). It also contains a few additional headers:

Access-Control-Request-Method – The HTTP method of the actual request. This request header is always included, even if the HTTP method is a simple HTTP method as defined earlier (GET, POST, HEAD).

Access-Control-Request-Headers – A comma-delimited list of non-simple headers that are included in the request.

The preflight request is a way of asking permissions for the actual request, before making the actual request. The server should inspect the two headers above to verify that both the HTTP method and the requested headers are valid and accepted.

If the HTTP method and headers are valid, the server should respond with the following:

Preflight Request:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Preflight Response:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin (required) – Like the simple response, the preflight response must include this header.

Access-Control-Allow-Methods (required) – Comma-delimited list of the supported HTTP methods. Note that although the preflight request only asks permisions for a single HTTP method, this reponse header can include the list of all supported HTTP methods. This is helpful because the preflight response may be cached, so a single preflight response can contain details about multiple request types.

Access-Control-Allow-Headers (required if the request has an Access-Control-Request-Headers header) – Comma-delimited list of the supported request headers. Like the Access-Control-Allow-Methods header above, this can list all the headers supported by the server (not only the headers requested in the preflight request).

Access-Control-Allow-Credentials (optional) – Same as simple request.

Access-Control-Max-Age (optional) – Making a preflight request on *every* request becomes expensive, since the browser is making two requests for every client request. The value of this header allows the preflight response to be cached for a specified number of seconds.

Once the preflight request gives permissions, the browser makes the actual request. The actual request looks like the simple request, and the response should be processed in the same way:

Actual Request:

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Actual Response:

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8

If the server wants to deny the CORS request, it can just return a generic response (like HTTP 200), without any CORS header. The server may want to deny the request if the HTTP method or headers requested in the preflight are not valid. Since there are no CORS-specific headers in the response, the browser assumes the request is invalid, and doesn’t make the actual request:

Preflight Request:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

Preflight Response:

// ERROR - No CORS headers, this is an invalid request!
Content-Type: text/html; charset=utf-8

If there is an error in the CORS request, the browser will fire the client’s onerrorevent handler. It will also print the following error to the console log:

XMLHttpRequest cannot load http://api.alice.com. Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

The browser doesn’t give you a lot of details on why the error occurred, it only tells you that something went wrong.

A word about security

While CORS lays the groundwork for making cross-domain requests, the CORS headers are not a substitute for sound security practices. You shouldn’t rely on the CORS header for securing resources on your site. Use the CORS headers to give the browser directions on cross-domain access, but use some other security mechanism, such as cookies or OAuth2, if you need additional security restrictions on your content.

CORS from JQuery

JQuery’s $.ajax() method can be used to make both regular XHR and CORS requests. A few notes about JQuery’s implementation:

  • JQuery’s CORS implementation doesn’t support IE’s XDomainRequest object. But there are JQuery plugins that enable this. Seehttp://bugs.jquery.com/ticket/8283 for details.
  • The $.support.cors boolean will be set to true if the browser supports CORS (This returns false in IE, see bullet above). This can be a quick way to check for CORS support.

Here’s sample code for making a CORS request with JQuery. The comments give more details on how certain properties interact with CORS.

$.ajax({

  // The 'type' property sets the HTTP method.
  // A value of 'PUT' or 'DELETE' will trigger a preflight request.
  type: 'GET',

  // The URL to make the request to.
  url: 'http://updates.html5rocks.com',

  // The 'contentType' property sets the 'Content-Type' header.
  // The JQuery default for this property is
  // 'application/x-www-form-urlencoded; charset=UTF-8', which does not trigger
  // a preflight. If you set this value to anything other than
  // application/x-www-form-urlencoded, multipart/form-data, or text/plain,
  // you will trigger a preflight request.
  contentType: 'text/plain',

  xhrFields: {
    // The 'xhrFields' property sets additional fields on the XMLHttpRequest.
    // This can be used to set the 'withCredentials' property.
    // Set the value to 'true' if you'd like to pass cookies to the server.
    // If this is enabled, your server must respond with the header
    // 'Access-Control-Allow-Credentials: true'.
    withCredentials: false
  },

  headers: {
    // Set any custom headers here.
    // If you set any non-simple headers, your server must include these
    // headers in the 'Access-Control-Allow-Headers' response header.
  },

  success: function() {
    // Here's where you handle a successful response.
  },

  error: function() {
    // Here's where you handle an error response.
    // Note that if the error was due to a CORS issue,
    // this function will still fire, but there won't be any additional
    // information about the error.
  }
});

Cross-Domain from Chrome Extensions

Chrome extensions support cross-domain requests in a two different ways:

    1. Include domain in manifest.json – Chrome extensions can make cross-domain requests to any domain *if* the domain is included in the “permissions” section of the manifest.json file:
      "permissions": [ "http://*.html5rocks.com"]

      The server doesn’t need to include any additional CORS headers or do any more work in order for the request to succeed.

  1. CORS request – If the domain is not in the manifest.json file, then the Chrome extension makes a standard CORS request. The value of the Origin header is “chrome-extension://[CHROME EXTENSION ID]”. This means requests from Chrome extensions are subject to the same CORS rules described in this article.

Known Issues

CORS support is still being actively developed in all browsers; here’s a list of known issues (as of 10/2/2013):

  1. FIXED XMLHttpRequests’ getAllResponseHeaders() doesn’t honor Access-Control-Expose-Headers – Firefox doesn’t return response headers when calling getAllResponseHeaders(). (Firefox bug). A similar bug in WebKit has been fixed.
  2. No error information provided to onerror handler – When the onerror handler is fired, the status code is 0, and there is no statusText. This may be by design, but it can be confusing when trying to debug why CORS requests are failing.

CORS Server Flowchart

The flowchart below shows how a server should make the decisions as to which headers to add to a CORS response. Click the image to see a larger version.

CORS Server Flowchart

CORS w/ Images

In Canvas and WebGL contexts, cross origin images can pose big problems. You can use the crossOrigin attribute on the image element to address much of them. Read Chromium Blog: Using Cross-domain images in WebGL and Mozilla Hacks: Using CORS to load WebGL textures from cross-domain images for the details. Implementation specifics can be found at the MDN page for CORS-enabled Image.

Resources

Here are some resources if you’d like to learn more about CORS:

Angular UI Bootstrap

Dependencies

This repository contains a set of native AngularJS directives based on Bootstrap’s markup and CSS. As a result no dependency on jQuery or Bootstrap’s JavaScript is required. The only required dependencies are:

  • AngularJS (minimal version 1.0.8)
  • Bootstrap CSS (tested with version 3.0.3). This version of the library (0.10.0) works only with Bootstrap CSS in version 3.x. 0.8.0 is the last version of this library that supports Bootstrap CSS in version 2.3.x.

Files to download

Build files for all directives are distributed in several flavours: minified for production usage, un-minified for development, with or without templates. All the options are described and can be downloaded from here.

Alternativelly, if you are only interested in a subset of directives, you can create your own build.

Whichever method you choose the good news that the overall size of a download is very small: <20kB for all directives (~5kB with gzip compression!)

Installation

As soon as you’ve got all the files downloaded and included in your page you just need to declare a dependency on the ui.bootstrap module:

angular.module('myModule', ['ui.bootstrap']);

You can fork one of the plunkers from this page to see a working example of what is described here.

CSS

Original Bootstrap’s CSS depends on empty href attributes to style cursors for several components (pagination, tabs etc.). But in AngularJS adding empty href attributes to link tags will cause unwanted route changes. This is why we need to remove empty href attributes from directive templates and as a result styling is not applied correctly. The remedy is simple, just add the following styling to your application:

.nav, .pagination, .carousel, .panel-title a { cursor: pointer; }

Accordion (ui.bootstrap.accordion)

The accordion directive builds on top of the collapse directive to provide a list of items, with collapsible bodies that are collapsed or expanded by clicking on the item’s header.

We can control whether expanding an item will cause the other items to close, using the close-others attribute on accordion.

The body of each accordion group is transcluded in to the body of the collapsible element.

html:

<div ng-controller="AccordionDemoCtrl">
  <label class="checkbox">
    <input type="checkbox" ng-model="oneAtATime">
    Open only one at a time
  </label>

  <accordion close-others="oneAtATime">
    <accordion-group heading="Static Header, initially expanded" is-open="true">
      This content is straight in the template.
    </accordion-group>
    <accordion-group heading="{{group.title}}" ng-repeat="group in groups">
      {{group.content}}
    </accordion-group>
    <accordion-group heading="Dynamic Body Content">
      <p>The body of the accordion group grows to fit the contents</p>
        <button class="btn btn-default btn-sm" ng-click="addItem()">Add Item</button>
        <div ng-repeat="item in items">{{item}}</div>
    </accordion-group>
    <accordion-group is-open="isopen">
        <accordion-heading>
            I can have markup, too! <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': isopen, 'glyphicon-chevron-right': !isopen}"></i>
        </accordion-heading>
        This is just some content to illustrate fancy headings.
    </accordion-group>
  </accordion>
</div>

Javascript

function AccordionDemoCtrl($scope) {
  $scope.oneAtATime = true;

  $scope.groups = [
    {
      title: "Dynamic Group Header - 1",
      content: "Dynamic Group Body - 1"
    },
    {
      title: "Dynamic Group Header - 2",
      content: "Dynamic Group Body - 2"
    }
  ];

  $scope.items = ['Item 1', 'Item 2', 'Item 3'];

  $scope.addItem = function() {
    var newItemNo = $scope.items.length + 1;
    $scope.items.push('Item ' + newItemNo);
  };
}

Alert (ui.bootstrap.alert)

Alert is an AngularJS-version of bootstrap’s alert.

This directive can be used to generate alerts from the dynamic model data (using the ng-repeat directive);

The presence of the “close” attribute determines if a close button is displayed

html:

<div ng-controller="AlertDemoCtrl">
  <alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">{{alert.msg}}</alert>
  <button class='btn btn-default' ng-click="addAlert()">Add Alert</button>
</div>

javascript:

function AlertDemoCtrl($scope) {
  $scope.alerts = [
    { type: 'danger', msg: 'Oh snap! Change a few things up and try submitting again.' },
    { type: 'success', msg: 'Well done! You successfully read this important alert message.' }
  ];

  $scope.addAlert = function() {
    $scope.alerts.push({msg: "Another alert!"});
  };

  $scope.closeAlert = function(index) {
    $scope.alerts.splice(index, 1);
  };

}

GAE: Java Runtime Environment : The sandbox

Java Runtime Environment : the sandbox

 [Fuente: https://developers.google.com/appengine/docs/java/#Java_The_sandbox]

Welcome to Google App Engine for Java! With App Engine, you can build web applications using standard Java technologies and run them on Google’s scalable infrastructure. The Java environment provides a Java Servlets interface and support for standard interfaces to the App Engine scalable datastore and services, such as JDO, JPA, JavaMail, and JCache. Standards support makes developing your application easy and familiar, and also makes porting your application to and from your own servlet environment straightforward.

  1. Introduction
  2. Selecting the Java runtime
  3. Services and packages
  4. Requests and domains
  5. Requests and servlets
  6. Request headers
  7. Responses
  8. The request timer
  9. The sandbox
  10. Class loader JAR ordering
  11. The JRE white list
  12. No signed JAR files
  13. Logging
  14. The environment
  15. Quotas and limits
  16. SPDY
  17. Scheduled tasks
  18. Java tools

Introduction

App Engine runs your Java web application using a Java 7 JVM in a safe “sandboxed” environment. App Engine invokes your app’s servlet classes to handle requests and prepare responses in this environment.

The Google Plugin for Eclipse adds new project wizards and debug configurations to your Eclipse IDE for App Engine projects. App Engine for Java makes it especially easy to develop and deploy world-class web applications using Google Web Toolkit (GWT). The Eclipse plugin comes bundled with the App Engine and GWT SDKs.

Third-party plugins are available for other Java IDEs as well. For NetBeans, see NetBeans support for Google App Engine. For IntelliJ, see Google App Engine Integration for IntelliJ. (These links take you to third-party websites.)

Warning: Applications that use Java 6 need to be migrated to Java 7. Existing applications that use the Java 6 runtime will continue to work, but updating these applications will require a whitelist request.

App Engine uses the Java Servlet standard for web applications. You provide your app’s servlet classes, JavaServer Pages (JSPs), static files and data files, along with the deployment descriptor (the web.xml file) and other configuration files, in a standard WAR directory structure. App Engine serves requests by invoking servlets according to the deployment descriptor.

The secured “sandbox” environment isolates your application for service and security. It ensures that apps can only perform actions that do not interfere with the performance and scalability of other apps. For instance, an app cannot spawn threads in some ways, write data to the local file system or make arbitrary network connections. An app also cannot use JNI or other native code. The JVM can execute any Java bytecode that operates within the sandbox restrictions.

If you haven’t already, see the Java Getting Started Guide for an interactive introduction to developing web applications with Java technologies and Google App Engine.

Selecting the Java runtime

App Engine knows to use the Java runtime environment for your application when you use the AppCfg tool from the Java SDK to upload the app.

The App Engine Java API is represented by the appengine-api-*.jar included with the SDK (where * represents the version of the API and the SDK). You select the version of the API your application uses by including this JAR in the application’s WEB-INF/lib/ directory. If a new version of the Java runtime environment is released that introduces changes that are not compatible with existing apps, that environment will have a new version number. Your application will continue to use the previous version until you replace the JAR with the new version (from a newer SDK) and re-upload the app.

Services and packages

App Engine provides scalable services that apps can use to store persistent data, access resources over the network, and perform other tasks like manipulating image data. You have the choice between two different data storage options differentiated by their availability and consistency guarantees. Where possible, the Java interfaces to these services conform to established standard APIs to allow for porting apps to and from App Engine. Each service also provides a complete low-level interface for implementing new interface adapters, or for direct access.

Apps can use the App Engine datastore for reliable, scalable persistent storage of data. The datastore supports two standard Java interfaces: Java Data Objects (JDO) 2.3 and Java Persistence API (JPA) 1.0. These interfaces are implemented using DataNucleus Access Platform, the open source implementation of these standards.

The App Engine Memcache provides fast, transient distributed storage for caching the results of datastore queries and calculations. The Java interface implements JCache (JSR 107).

Apps use the URL Fetch service to access resources over the web, and to communicate with other hosts using the HTTP and HTTPS protocols. Java apps can simply use java.net.URLConnection and related classes from the Java standard library to access this service.

An app can use the Mail service to send email messages on behalf of the application’s administrators, or on behalf of the currently signed-in user. Java apps use the JavaMailinterface for sending email messages.

The Images service lets applications transform and manipulate image data in several formats, including cropping, rotating, resizing, and photo color enhancement. The service can handle CPU-intensive image processing tasks, leaving more resources available for the application server to handle web requests. (You can also use any JVM-based image processing software on the application server, provided it operates within the sandbox restrictions.)

An application can use Google Accounts for user authentication. Google Accounts handles user account creation and sign-in, and a user that already has a Google account (such as a GMail account) can use that account with your app. An app can detect when the current user is signed in, and can access the user’s email address. Java applications can use security constraints in the deployment descriptor to control access via Google Accounts, and can detect whether the user is signed in and get the email address using the getUserPrincipal() method on the servlet request object. An app can use the low-level Google Accounts API to generate sign-in and sign-out URLs, and to get a user data object suitable for storage in the datastore.

Requests and domains

App Engine determines that an incoming request is intended for your application using the domain name of the request. A request whose domain name ishttp://your_app_id.appspot.com is routed to the application whose ID is your_app_id. Every application gets an appspot.com domain name for free.

appspot.com domains also support subdomains of the form subdomain-dot-your_app_id.appspot.com, where subdomain can be any string allowed in one part of a domain name (not .). Requests sent to any subdomain in this way are routed to your application.

You can set up a custom top-level domain using Google Apps. With Google Apps, you assign subdomains of your business’s domain to various applications, such as Google Mail or Sites. You can also associate an App Engine application with a subdomain. For convenience, you can set up a Google Apps domain when you register your application ID, or later from the Administrator Console. See Deploying your Application on your Google Apps URL for more information.

Requests for these URLs all go to the version of your application that you have selected as the default version in the Administration Console. Each version of your application also has its own URL, so you can deploy and test a new version before making it the default version. The version-specific URL uses the version identifier from your app’s configuration file in addition to the appspot.com domain name, in this pattern: http://version_id-dot-latest-dot-your_app_id.appspot.com You can also use subdomains with the version-specific URL: http://subdomain-dot-version_id-dot-latest-dot-your_app_id.appspot.com

The domain name used for the request is included in the request data passed to the application. If you want your app to respond differently depending on the domain name used to access it (such as to restrict access to certain domains, or redirect to an official domain), you can check the request data (such as the Host request header) for the domain from within the application code and respond accordingly.

If your app uses backends, you can address requests to a specific backend and a specific instance with that backend. For more information about backend addressability, please see Properties of Backends.

Please note that in April of 2013, Google stopped issuing SSL certificates for double-wildcard domains hosted at appspot.com (i.e. *.*.appspot.com). If you rely on such URLs for HTTPS access to your application, please change any application logic to use “-dot-” instead of “.”. For example, to access version “1” of application “myapp” use “https://1-dot-myapp.appspot.com” instead of “https://1.myapp.appspot.com.” If you continue to use “https://1.myapp.appspot.com” the certificate will not match, which will result in an error for any User-Agent that expects the URL and certificate to match exactly.

Requests and servlets

When App Engine receives a web request for your application, it invokes the servlet that corresponds to the URL, as described in the application’s deployment descriptor (the web.xml file in the WEB-INF/ directory). It uses the Java Servlet API to provide the request data to the servlet, and accept the response data.

App Engine uses multiple web servers to run your application, and automatically adjusts the number of servers it is using to handle requests reliably. A given request may be routed to any server, and it may not be the same server that handled a previous request from the same user.

By default, each web server processes only one request at a time. If you mark your application as thread-safe, App Engine may dispatch multiple requests to each web server in parallel. To do so, simply add a <threadsafe>true</threadsafe> element to appengine-web.xml as described in Using Concurrent Requests.

The following example servlet class displays a simple message on the user’s browser.

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/plain");
        resp.getWriter().println("Hello, world");
    }
}

Request headers

An incoming HTTP request includes the HTTP headers sent by the client. For security purposes, some headers are sanitized or amended by intermediate proxies before they reach the application.

The following headers are removed from the request:

  • Accept-Encoding
  • Connection
  • Keep-Alive
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding

In addition, the header Strict-Transport-Security is removed from requests served to any domains other than appspot.com or *.appspot.com.

These headers relate to the transfer of the HTTP data between the client and server, and are transparent to the application. For example, the server may automatically send a gzipped response, depending on the value of the Accept-Encoding request header. The application itself does not need to know which content encodings the client can accept.

As a service to the app, App Engine adds some headers:

X-AppEngine-Country
Country from which the request originated, as an ISO 3166-1 alpha-2 country code. App Engine determines this code from the client’s IP address.
X-AppEngine-Region
Name of region from which the request originated. This value only makes sense in the context of the country in X-AppEngine-Country. For example, if the country is “US” and the region is “ca”, that “ca” means “California”, not Canada.
X-AppEngine-City
Name of the city from which the request originated. For example, a request from the city of Mountain View might have the header value mountain view.
X-AppEngine-CityLatLong
Latitude and longitude of the city from which the request originated. This string might look like “37.386051,-122.083851” for a request from Mountain View.

Responses

App Engine calls the servlet with a request object and a response object, then waits for the servlet to populate the response object and return. When the servlet returns, the data on the response object is sent to the user.

App Engine does not support sending data to the client, performing more calculations in the application, then sending more data. In other words, App Engine does not support “streaming” data in response to a single request.

Dynamic responses are limited to 32MB. If a script handler generates a response larger than this limit, the server sends back an empty response with a 500 Internal Server Error status code. This limitation does not apply to responses that serve data from the Blobstore or Google Cloud Storage.

If the client sends HTTP headers with the request indicating that the client can accept compressed (gzipped) content, App Engine compresses the response data automatically and attaches the appropriate response headers. It uses both the Accept-Encoding and User-Agent request headers to determine if the client can reliably receive compressed responses. Custom clients can indicate that they are able to receive compressed responses by specifying both Accept-Encoding and User-Agent headers with a value ofgzip. The Content-Type of the response is also used to determine whether compression is appropriate; in general, text-based content types are compressed, whereas binary content types are not.

The following headers are ignored and removed from the response:

  • Connection
  • Content-Encoding
  • Content-Length
  • Date
  • Keep-Alive
  • Proxy-Authenticate
  • Server
  • Trailer
  • Transfer-Encoding
  • Upgrade

In addition, the header Strict-Transport-Security is removed from responses served from any domains other than *.appspot.com.

Headers with non-ASCII characters in either the name or value are also removed. In addition, the following headers are added or replaced in the response:

Cache-ControlExpires and Vary
These headers specify caching policy to intermediate web proxies (such as Internet Service Providers) and browsers. If your script sets these headers, they will usually be unmodified, unless the response has a Set-Cookie header, or is generated for a user who is signed in using an administrator account. Static handlers will set these headers as directed by the configuration file. If you do not specify a Cache-Control, the server may set it to private, and add a Vary: Accept-Encoding header.If you have a Set-Cookie response header, the Cache-Control header will be set to private (if it is not already more restrictive) and the Expires header will be set to the current date (if it is not already in the past). Generally, this will allow browsers to cache the response, but not intermediate proxy servers. This is for security reasons, since if the response was cached publicly, another user could subsequently request the same resource, and retrieve the first user’s cookie.
Content-Encoding
Depending upon the request headers and response Content-Type, the server may automatically compress the response body, as described above. In this case, it adds aContent-Encoding: gzip header to indicate that the body is compressed.
Content-Length or Transfer-Encoding
The server always ignores the Content-Length header returned by the application. It will either set Content-Length to the length of the body (after compression, if compression is applied), or delete Content-Length, and use chunked transfer encoding (adding a Transfer-Encoding: chunked header).
Content-Type
If not specified by the application, the server will set a default Content-Type: text/html header.
Date
Set to the current date and time.
Server
Set to Google Frontend.

If you access your site while signed in using an administrator account, App Engine includes per-request statistics in the response headers:

X-AppEngine-Estimated-CPM-US-Dollars
An estimate of what 1,000 requests similar to this request would cost in US dollars.
X-AppEngine-Resource-Usage
The resources used by the request, including server-side time as a number of milliseconds.

Responses with resource usage statistics will be made uncacheable.

If the X-AppEngine-BlobKey header is in the application’s response, it and the optional X-AppEngine-BlobRange header will be used to replace the body with all or part of a blobstore blob’s content. If Content-Type is not specified by the application, it will be set to the blob’s MIME type. If a range is requested, the response status will be changed to206 Partial Content, and a Content-Range header will be added. The X-AppEngine-BlobKey and X-AppEngine-BlobRange headers will be removed from the response. You do not normally need to set these headers yourself, as the blobstore_handlers.BlobstoreDownloadHandler class sets them. See Serving a Blob for details.

The request timer

A request handler has a limited amount of time to generate and return a response to a request, typically around 60 seconds. Once the deadline has been reached, the request handler is interrupted.

The Java runtime environment interrupts the servlet by throwing a com.google.apphosting.api.DeadlineExceededException. If there is no request handler to catch this exception, the runtime environment will return an HTTP 500 server error to the client.

If there is a request handler and the DeadlineExceededException is caught, then the runtime environment gives the request handler time (less than a second) to prepare a custom response. If the request handler takes more than a second after raising the exception to prepare a custom response, a HardDeadlineExceededError will be raised.

Both DeadlineExceededExceptions and HardDeadlineExceededErrors will force termination of the request and kill the instance.

To find out how much time remains before the deadline, the application can import com.google.apphosting.api.ApiProxy and callApiProxy.getCurrentEnvironment().getRemainingMillis(). This is useful if the application is planning to start on some work that might take too long; if you know it takes five seconds to process a unit of work but getRemainingMillis() returns less time, there’s no point starting that unit of work.

While a request can take as long as 60 seconds to respond, App Engine is optimized for applications with short-lived requests, typically those that take a few hundred milliseconds. An efficient app responds quickly for the majority of requests. An app that doesn’t will not scale well with App Engine’s infrastructure.

Refer to Dealing with DeadlineExceededErrors for common DeadlineExceededError causes and suggested workarounds.

Backends allow you to avoid this request timer; with backends, there is no time limit for generating and returning a request.

The sandbox

To allow App Engine to distribute requests for applications across multiple web servers, and to prevent one application from interfering with another, the application runs in a restricted “sandbox” environment. In this environment, the application can execute code, store and query data in the App Engine datastore, use the App Engine mail, URL fetch and users services, and examine the user’s web request and prepare the response.

An App Engine application cannot:

  • write to the filesystem. Applications must use the App Engine datastore for storing persistent data. Reading from the filesystem is allowed, and all application files uploaded with the application are available.
  • respond slowly. A web request to an application must be handled within a few seconds. Processes that take a very long time to respond are terminated to avoid overloading the web server.
  • make other kinds of system calls.

Threads

A Java application can create a new thread, but there are some restrictions on how to do it. These threads can’t “outlive” the request that creates them. (On a backend server, an application can spawn a background thread, a thread that can “outlive” the request that creates it.)

An application can

or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., callExecutors.newCachedThreadPool(factory)).

However, you must use one of the methods on ThreadManager to create your threads. You cannot invoke new Thread() yourself or use the default thread factory.

An application can perform operations against the current thread, such as thread.interrupt().

Each request is limited to 50 concurrent request threads.

The Filesystem

A Java application cannot use any classes used to write to the filesystem, such as java.io.FileWriter. An application can read its own files from the filesystem using classes such as java.io.FileReader. An application can also access its own files as “resources”, such as with Class.getResource() or ServletContext.getResource().

Only files that are considered “resource files” are accessible to the application via the filesystem. By default, all files in the WAR are “resource files.” You can exclude files from this set using the appengine-web.xml file.

java.lang.System

Features of the java.lang.System class that do not apply to App Engine are disabled.

The following System methods do nothing in App Engine: exit()gc()runFinalization()runFinalizersOnExit()

The following System methods return nullinheritedChannel()console()

An app cannot provide or directly invoke any native JNI code. The following System methods raise a java.lang.SecurityExceptionload()loadLibrary(),setSecurityManager()

Reflection

An application is allowed full, unrestricted, reflective access to its own classes.

It may query any private members, call the method java.lang.reflect.AccessibleObject.setAccessible(), and read/set private members.

An application can also reflect on JRE and API classes, such as java.lang.String and javax.servlet.http.HttpServletRequest. However, it can only access public members of these classes, not protected or private.

An application cannot reflect against any other classes not belonging to itself, and it can not use the setAccessible() method to circumvent these restrictions.

Custom class loading

Custom class loading is fully supported under App Engine. An application is allowed to define its own subclass of ClassLoader that implements application-specific class loading logic. Please be aware, though, that App Engine overrides all ClassLoaders to assign the same permissions to all classes loaded by your application. If you perform custom class loading, be cautious when loading untrusted third-party code.

Class loader JAR ordering

Sometimes, it may be necessary to redefine the order in which JAR files are scanned for classes in order to resolve collisions between class names. In these cases, loading priority can be granted to specific JAR files by adding a <class-loader-config> element containing <priority-specifier> elements in the appengine-web.xml file. For example:

<class-loader-config>
        <priority-specifier filename="mailapi.jar"/>
</class-loader-config>

This places “mailapi.jar” as the first JAR file to be searched for classes, barring those in the directory war/WEB-INF/classes/.

If multiple JAR files are prioritized, their original loading order (with respect to each other) will be used. In other words, the order of the <priority-specifier> elements themselves does not matter.

The JRE white list

Access to the classes in the Java standard library (the Java Runtime Environment, or JRE) is limited to the classes in the App Engine JRE White List.

No signed JAR files

App Engine’s precompilation isn’t compatible with signed JAR files. If your application is precompiled (the default), it can’t load signed JAR files. If the application tries to load a signed JAR, at runtime App Engine will generate an exception like

java.lang.SecurityException: SHA1 digest error for com/example/SomeClass.class
    at com.google.appengine.runtime.Request.process-d36f818a24b8cf1d(Request.java)
    at sun.security.util.ManifestEntryVerifier.verify(ManifestEntryVerifier.java:210)
    at java.util.jar.JarVerifier.processEntry(JarVerifier.java:218)
    at java.util.jar.JarVerifier.update(JarVerifier.java:205)
    at java.util.jar.JarVerifier$VerifierStream.read(JarVerifier.java:428)
    at sun.misc.Resource.getBytes(Resource.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:273)
    at sun.reflect.GeneratedMethodAccessor5.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:616)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:266)

There are two ways to work around this:

Logging

Your application can write information to the application logs using java.util.logging.Logger. Log data for your application can be viewed and analyzed using the Administration Console, or downloaded using appcfg.sh request_logs. Each request logged is assigned a request ID, a globally unique identifier based on the request’s start time. The Admin Console can recognize the Logger class’s log levels, and interactively display messages at different levels.

Everything the servlet writes to the standard output stream (System.out) and standard error stream (System.err) is captured by App Engine and recorded in the application logs. Lines written to the standard output stream are logged at the “INFO” level, and lines written to the standard error stream are logged at the “WARNING” level. Any logging framework (such as log4j) that logs to the output or error streams will work. However, for more fine-grained control of the Admin Console’s log level display, the logging framework must use a java.util.logging adapter.

import java.util.logging.Logger;
// ...

public class MyServlet extends HttpServlet {
    private static final Logger log = Logger.getLogger(MyServlet.class.getName());

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        log.info("An informational message.");

        log.warning("A warning message.");

        log.severe("An error message.");
    }
} 

The App Engine Java SDK includes a template logging.properties file, in the appengine-java-sdk/config/user/ directory. To use it, copy the file to your WEB-INF/classes directory (or elsewhere in the WAR), then the system property java.util.logging.config.file to "WEB-INF/logging.properties" (or whichever path you choose, relative to the application root). You can set system properties in the appengine-web.xml file, as follows:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    ...

    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
    </system-properties>

</appengine-web-app>

The servlet logs messages using the INFO log level (using log.info()). The default log level is WARNING, which suppresses INFO messages from the output. To change the log level, edit the logging.properties file. See the Guestbook Form application for a specific example on how to set log levels.

The Google Plugin for Eclipse new project wizard creates these logging configuration files for you, and copies them to WEB-INF/classes/ automatically. Forjava.util.logging, you must set the system property to use this file.

The environment

All system properties and environment variables are private to your application. Setting a system property only affects your application’s view of that property, and not the JVM’s view.

You can set system properties and environment variables for your app in the deployment descriptor.

App Engine sets several system properties that identify the runtime environment:

  • com.google.appengine.runtime.environment is "Production" when running on App Engine, and "Development" when running in the development server.In addition to using System.getProperty(), you can access system properties using our type-safe API. For example:
    if (SystemProperty.environment.value() ==
        SystemProperty.Environment.Value.Production) {
        // The app is running on App Engine...
    }
    
  • com.google.appengine.runtime.version is the version ID of the runtime environment, such as "1.3.0". You can get the version by invoking the following:String version = SystemProperty.version.get();
  • com.google.appengine.application.id is the application’s ID. You can get the ID by invoking the following: String ID = SystemProperty.applicationId.get();
  • com.google.appengine.application.version is the major and minor version of the currently running application module, as “X.Y”. The major version number (“X”) is specified in the module’s appengine-web.xml file. The minor version number (“Y”) is set automatically when each version of the app is uploaded to App Engine. You can get the ID by invoking the following: String ID = SystemProperty.applicationVersion.get();On the development web server, the major version returned is always the default module’s version, and the minor version is always “1”.

App Engine also sets the following system properties when it initializes the JVM on an app server:

  • file.separator
  • path.separator
  • line.separator
  • java.version
  • java.vendor
  • java.vendor.url
  • java.class.version
  • java.specification.version
  • java.specification.vendor
  • java.specification.name
  • java.vm.vendor
  • java.vm.name
  • java.vm.specification.version
  • java.vm.specification.vendor
  • java.vm.specification.name
  • user.dir

Instance IDs

You can retrieve the ID of the instance handling a request using this code:

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.instance.id")

In the production environment, a logged-in admin can use the ID in a url: http://[INSTANCE_ID].myApp.appspot.com/. The request will be routed to that specific instance. If the instance cannot handle the request it returns an immediate 503.

Request IDs

At the time of the request, you can save the request ID, which is unique to the request. The request ID can be used later to correlate a request with the logs for that request.

The following code shows how to get the request ID in the context of a request:

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.runtime.request_log_id")

Quotas and limits

Google App Engine automatically allocates resources to your application as traffic increases. However, this is bound by the following restrictions:

  • App Engine reserves automatic scaling capacity for applications with low latency, where the application responds to requests in less than one second. Applications with very high latency (over one second per request for many requests) and high throughput require Silver, Gold, or Platinum support. Customers with this level of support can request higher throughput limits by contacting their support representative.
  • Applications that are heavily CPU-bound may also incur some additional latency in order to efficiently share resources with other applications on the same servers. Requests for static files are exempt from these latency limits.

Each incoming request to the application counts toward the Requests limit. Data sent in response to a request counts toward the Outgoing Bandwidth (billable) limit.

Both HTTP and HTTPS (secure) requests count toward the RequestsIncoming Bandwidth (billable), and Outgoing Bandwidth (billable) limits. The Quota Details page of the Admin Console also reports Secure RequestsSecure Incoming Bandwidth, and Secure Outgoing Bandwidth as separate values for informational purposes. Only HTTPS requests count toward these values. See the Quotas page, and the “Quota Details” section of the Admin Console for more information.

In addition to system-wide safety limits, the following limits apply specifically to the use of request handlers:

Limit Amount
request size 32 megabytes
response size 32 megabytes
request duration 60 seconds
maximum total number of files (app files and static files) 10,000 total
1,000 per directory
maximum size of an application file 32 megabytes
maximum size of a static file 32 megabytes
maximum total size of all application and static files first 1 gigabyte is free
$ 0.026 per gigabyte per month after first 1 gigabyte

SPDY

App Engine applications will automatically use the SPDY protocol when accessed over SSL by a browser that supports SPDY. This is a replacement for HTTP designed by Google and intended to reduce the latency of web page downloads. The use of SPDY should be entirely transparent to both applications and users (applications can be written as if normal HTTP was being used). For more information, see the SPDY project page.

Scheduled tasks

An application can configure scheduled tasks that will call URLs of the application at specified intervals. For more on this, see Cron Jobs.

Java tools

The App Engine Java SDK includes tools for testing your application, uploading your application files, and downloading log data. The SDK also includes a component for Apache Antto simplify tasks common to App Engine projects. The Google Plugin for Eclipse adds features to the Eclipse IDE for App Engine development, testing and deployment, and includes the complete App Engine SDK. The Eclipse plugin also makes it easy to develop Google Web Toolkit applications and run them on App Engine.

The App Engine Java SDK also has a plugin for supporting development with Apache Maven.

The development server runs your application on your local computer for development and testing. The server simulates the App Engine datastore, services and sandbox restrictions. The development server can also generate configuration for datastore indexes based on the queries the app performs during testing.

A multipurpose tool called AppCfg handles all command-line interaction with your application running on App Engine. AppCfg can upload your application to App Engine, or just update the datastore index configuration so you can build new indexes before updating the code. It can also download the app’s log data, so you can analyze your app’s performance using your own tools.

Bootstrap

[Fuente: http://getbootstrap.com/getting-started/]

Bootstrap es un potente framework front-end para hacer desarrollos web rápidos, creado por Mark Otto y Jacob Thornton, and maintained by the core team with the massive support and involvement of the community.

Bootstrap tiene varias formas de empezar rápidamente con él, cada una dependiendo del diferente nivel de aprendizaje y del caso de uso

Bootstrap

Compiled and minified CSS, JavaScript, and fonts. No docs or original source files are included.

Download Bootstrap

Source code

Source Less, JavaScript, and font files, along with our docs. Requires a Less compiler and some setup.

Download source

Sass

Bootstrap ported from Less to Sass for easy inclusion in Rails, Compass, or Sass-only projects.

Download Sass

Currently v3.1.1.

Bootstrap CDN

The folks over at MaxCDN graciously provide CDN support for Bootstrap’s CSS and JavaScript. Just use these Bootstrap CDN links.

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

<!-- Optional theme -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">

<!-- Latest compiled and minified JavaScript -->
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>

Install with Bower

Install and manage Bootstrap’s Less, CSS, JavaScript, and fonts using Bower.

$ bower install bootstrap

sdsdsdss

[Fuente: http://librosweb.es/bootstrap_3/]

1.2. Contenidos de Bootstrap

Bootstrap se puede descargar de dos maneras, compilado o mediante el código fuente original. Dependiendo de la forma que hayas elegido, verás una estructura de directorios u otra. En esta sección se muestran los detalles de cada una de ellas.

ADVERTENCIA Todos los plugins JavaScript de Bootstrap requieren la librería jQuery para funcionar, por lo que deberás incluirlo en tus plantillas, tal y como se muestra en los ejemplos de las siguientes secciones. Para saber qué versiones concretas de jQuery se soportan, consulta el contenido de nuestro archivo bower.json.

1.2.1. Contenidos de la versión compilada de Bootstrap

Después de descomprimir el archivo que te has descargado con la versión compilada de Bootstrap, verás la siguiente estructura de archivos y directorios:

bootstrap/
├── css/
│   ├── bootstrap.css
│   ├── bootstrap.min.css
│   ├── bootstrap-theme.css
│   └── bootstrap-theme.min.css
├── js/
│   ├── bootstrap.js
│   └── bootstrap.min.js
└── fonts/
    ├── glyphicons-halflings-regular.eot
    ├── glyphicons-halflings-regular.svg
    ├── glyphicons-halflings-regular.ttf
    └── glyphicons-halflings-regular.woff

Estos archivos son la forma más sencilla de utilizar Bootstrap en cualquier proyecto web. Para cada archivo se ofrecen dos variantes: los archivos compilados (cuyo nombre es bootstrap.*) y los archivos compilados + comprimidos (cuyo nombre es bootstrap.min.*). También se incluyen las fuentes de los iconos del proyecto Glyphicons y el tema opcional de Bootstrap.

1.2.2. Contenidos de la versión original de Bootstrap

La versión original de Bootstrap incluye, además de las versiones compiladas de los archivos CSS y JavaScript, las versiones originales de esos mismos archivos y toda la documentación. Tras descomprimir el archivo que te has descargado con la versión original de Bootstrap, verás la siguiente estructura de archivos y directorios:

bootstrap/
├── less/
├── js/
├── fonts/
├── dist/
│   ├── css/
│   ├── js/
│   └── fonts/
└── docs/
    └── examples/

Los directorios less/js/ y fonts/ contienen el código fuente utilizado para generar los archivos CSS, JavaScript y las fuentes. El directorio dist/ contiene los mismos archivos que se han mostrado en la sección anterior para la versión compilada de Bootstrap. El directorio docs/ incluye el código fuente de la documentación de Bootstrap y un directorio llamado examples/ con varios ejemplos de uso. El resto de archivos están relacionados con otros temas secundarios, como por ejemplo las licencias del código.

1.3. La primera plantilla Bootstrap

Si eres nuevo en Bootstrap, puedes empezar con la plantilla HTML básica que se muestra a continuación o puedes echar un vistazo a los ejemplos de plantillas que hemos preparado. La idea de estos ejemplos es que los utilices como punto de partida para crear tus propios diseños.

El siguiente código HTML muestra una plantilla muy sencilla creada con Bootstrap:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Plantilla básica de Bootstrap</title>

    <!-- CSS de Bootstrap -->
    <link href="css/bootstrap.min.css" rel="stylesheet" media="screen">

    <!-- librerías opcionales que activan el soporte de HTML5 para IE8 -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <h1>¡Hola mundo!</h1>

    <!-- Librería jQuery requerida por los plugins de JavaScript -->
    <script src="http://code.jquery.com/jquery.js"></script>

    <!-- Todos los plugins JavaScript de Bootstrap (también puedes
         incluir archivos JavaScript individuales de los únicos
         plugins que utilices) -->
    <script src="js/bootstrap.min.js"></script>
  </body>
</html>

1.3.1. Ejemplos de plantillas creadas con Bootstrap

Partiendo de la primera plantilla básica mostrada anteriormente se pueden crear muchos otros diseños de página. La siguiente lista muestra cómo crear algunos de los diseños más utilizados en los sitios web actuales: http://getbootstrap.com/getting-started/#examples

1.4. La comunidad Bootstrap

Utiliza los siguientes recursos para estar al tanto de las novedades de Bootstrap y mantener el contacto con otros miembros de la comunidad.

  • El blog oficial de Bootstrap.
  • Chatea con otros miembros de la comunidad a través de nuestro canal ##twitter-bootstrap en el servidor irc.freenode.net.
  • Descubre algunos de los proyectos más alucinantes creados con Bootstrap en el sitio Bootstrap Expo.

También puedes seguir nuestra cuenta oficial en Twitter: @twbootstrap.

1.5.  Desactivando el diseño responsive

¿No quieres que tu sitio o aplicación web modifique su aspecto según el dispositivo utilizado por el usuario? En ese caso, y con un poco de esfuerzo, puedes desactivar las características responsive de Bootstrap para que los usuarios con móvil vean el mismo sitio que los usuarios con un navegador de escritorio.

1.5.1. Cómo desactivar el diseño responsive de Bootstrap

Aplica los siguientes pasos para desactivar el diseño responsive de Bootstrap y echa un vistazo al ejemplo que se muestra después:

  1. Elimina de tus páginas (o simplemente no añadas) la etiqueta <meta name="viewport" ... > que se explica en los próximos capítulos.
  2. Elimina la propiedad max-width de todos los elementos .container aplicando el estilo max-width: none !important; y estableciendo una anchura normal con width: 970px;. Obviamente, debes añadir estos estilos después del CSS por defecto aplicado por Bootstrap.
  3. Si utilizas menús y barras de navegación, debes eliminar todos los estilos que hacen que se compriman en dispositivos pequeños. Como este cambio es enorme, será mejor que veas los estilos CSS del ejemplo que se muestra más adelante.
  4. Utiliza las clases .col-xs-* (xs = extra-small) para tus diseños basados en rejillas en vez de las otras clases para dispositivos medianos o grandes. No te preocupes porque estas clases .col-xs-* escalan bien para cualquier resolución.

En cualquier caso, si utilizas IE8 debes seguir cargando la librería Respond.js, ya que las media queriessiguen siendo necesarias a pesar de los cambios anteriores.

Para que sean más fáciles de entender los cambios anteriores, se muestra a continuación un ejemplo completo de cómo aplicarlos en una página real: Ver ejemplo de diseño no responsive. No olvides echar un vistazo a su código porque se han resaltado claramente todos los cambios realizados.

1.6. Actualización de Bootstrap 2.X a 3.0

Esta sección está pensada para aquellos diseñadores que quieren actualizar sus proyectos a Bootstrap 3 desde la anterior versión. Además de mencionar los cambios más importantes, se incluyen varias tablas sobre la equivalencia entre Bootstrap 2 y 3.

1.6.1. Principales cambios en las clases CSS

Clase de Bootstrap 2.x Clase de Bootstrap 3.0
.container-fluid .container
.row-fluid .row
.span* .col-md-*
.offset* .col-md-offset-*
.brand .navbar-brand
.nav-collapse .navbar-collapse
.nav-toggle .navbar-toggle
.btn-navbar .navbar-btn
.hero-unit .jumbotron
.icon-* .glyphicon .glyphicon-*
.btn .btn .btn-default
.btn-mini .btn-xs
.btn-small .btn-sm
.btn-large .btn-lg
.visible-phone .visible-sm
.visible-tablet .visible-md
.visible-desktop .visible-lg
.hidden-phone .hidden-sm
.hidden-tablet .hidden-md
.hidden-desktop .hidden-lg
.input-small .input-sm
.input-large .input-lg
.checkbox.inline .radio.inline .checkbox-inline .radio-inline
.input-prepend .input-append .input-group
.add-on .input-group-addon
.thumbnail .img-thumbnail
ul.unstyled .list-unstyled
ul.inline .list-inline

1.6.2. Nuevos elementos

Bootstrap 3 añade nuevos elementos y cambia algunos de los ya existentes. La siguiente tabla resume las clases CSS que se han añadido o modificado.

Elemento Clases CSS
Paneles .panel .panel-default .panel-body .panel-title .panel-heading .panel-footer .panel-collapse
Grupos de listas .list-group .list-group-item .list-group-item-text .list-group-item-heading
Glyphicons .glyphicon
Jumbotron .jumbotron
Rejilla diminuta (<768 px) .col-xs-*
Rejilla pequeña (>768 px) .col-sm-*
Rejilla mediana (>992 px) .col-md-*
Rejilla grande (>1200 px) .col-lg-*
Márgenes .col-sm-offset-* .col-md-offset-* .col-lg-offset-*
Desplazamiento hacia la derecha .col-sm-push-* .col-md-push-* .col-lg-push-*
Desplazamiento hacia la izquierda .col-sm-pull-* .col-md-pull-* .col-lg-pull-*
Grupos de <input> .input-group .input-group-addon .input-group-btn
Controles de formulario .form-control .form-group
Grupos de botones .btn-group-xs .btn-group-sm .btn-group-lg
Texto de los .navbar .navbar-text
Cabecera de los .navbar .navbar-header
Pestañas justificadas .nav-justified
Imágenes responsive .img-responsive
Filas de tabla contextuales .success .danger .warning .active
Paneles contextuales .panel-success .panel-danger .panel-warning .panel-info
Ventanas modales .modal-dialog .modal-content
Imágenes en miniatura .img-thumbnail
Elementos .well .well-sm .well-lg
Enlaces de alerta .alert-link

1.6.3. Elementos eliminados

Los siguientes elementos se han eliminado o han cambiado en Bootstrap 3.

Elemento existente en 2.x Equivalente en 3.0
.form-actions (no existe)
.form-search (no existe)
.container-fluid .container (ahora todas las rejillas son fluidas)
.row-fluid .row (ahora todas las rejillas son fluidas)
.navbar-inner (no existe)
.dropdown-submenu (no existe)
.tabs-left .tabs-right .tabs-below (no existe)

1.6.4. Otros cambios importantes

Bootstrap 3 incluye decenas de pequeños cambios que al principio pueden pasar desapercibidos. Todos los estilos y comportamientos principales de Bootstrap se han ajustado para que sean más flexibles y encajen en la nueva estrategia “mobile first” en la que lo más importante son los dispositivos móviles.

  • Los campos de formulario de texto ahora tienen una anchura del 100%. Para controlar su anchura, enciérralos con la etiqueta <div></div>.
  • La clase .badge ya no tiene sufijos semánticos (-success-primary,etc.).
  • El botón con aspecto normal requiere, además de la clase .btn, la clase .btn-default.
  • Tanto .container como .row ahora se comportan de forma fluída porque están basados en porcentajes.
  • Las imágenes ya no son responsive por defecto. Añade la clase .img-responsive para convertir una imagen en responsive.
  • Los iconos, que ahora utilizan la clase .glyphicon, se crean con una fuente especial y no con imágenes. Cada icono requiere una clase CSS genérica y otra específica. Para mostrar por ejemplo el icono de un asterisco tienes que utilizar las siguientes clases: .glyphicon .glyphicon-asterisk.
  • El componente Typeahead se ha eliminado y ahora se utiliza Twitter Typeahead.
  • El código HTML de los elementos modales ha cambiado mucho. Las secciones .modal-header.modal-body y .modal-footer ahora se encierran con las clases .modal-content y .modal-dialog para mejorar su aspecto en los móviles.
  • Los eventos de JavaScript ahora usan namespaces. Para utilizar por ejemplo el evento show de un elemento modal, utiliza el evento show.bs.modal; para el evento shown de las pestañas se utilizashown.bs.tab, etc.

El sitio web Bootply dispone de más información y ejemplos de código para actualizar de Bootstrap 2 a Bootstrap 3.

1.7. Compatibilidad con los navegadores

Bootstrap ha sido pensado para utilizarse en las versiones más recientes de los navegadores de escritorio y navegadores móviles. Aunque también funciona con los navegadores más antiguos, en estos casos es posible que algunos componentes se vean peor de lo esperado.

Concretamente, Bootstrap soporta las versiones más recientes de los siguientes navegadores y plataformas:

Chrome Firefox Internet Explorer Opera Safari
Android
iOS
Mac OS X
Windows

Bootstrap también debería funcionar bien en Chromium (Linux) y Internet Explorer 7 (Windows), aunque no están soportados oficialmente.

1.7.2. Internet Explorer 8 y 9

Los navegadores Internet Explorer 8 y 9 también están soportados, pero debes tener en cuenta que muchas propiedades de CSS3 y elementos de HTML5 no funcionan en estos navegadores. Además, Internet Explorer 8 requiere el uso de la librería respond.js para que el diseño web responsive funcione bien.

Funcionalidad Internet Explorer 8 Internet Explorer 9
border-radius ✘ No soportado ✔ Soportado
box-shadow ✘ No soportado ✔ Soportado
transform ✘ No soportado ✔ Soportado con el prefijo -ms
transition ✘ No soportado ✘ No soportado
placeholder ✘ No soportado ✘ No soportado

Visita el sitio web Can I use… obtener más información sobre el soporte de todas las propiedades de CSS3 y HTML5 en cada navegador.

1.7.3. Internet Explorer 8 y Respond.js

Cuando utilices Respond.js con Internet Explorer 8, debes tener en cuenta las siguientes limitaciones.

1.7.3.1. Respond.js y el CSS alojado en otro dominio

Si utilizas archivos CSS alojados en dominios o subdominios diferentes al utilizado para enlazar Respond.js(por ejemplo porque usas una CDN) entonces debes realizar ciertas configuraciones adicionales, tal y como se explica en la documentación de Respond.js.

1.7.3.2.  Respond.js y file://

Debido a las restricciones de seguridad, Respond.js no funciona cuando ves las páginas localmente en tu propio navegador mediante el protocolo file:// (por ejemplo al pinchar dos veces sobre un archivo HTML de tu ordenador). Para probar las funcionalidades responsive en Internet Explorer 8, asegúrate de ver la página a través del protocolo http:// o https://. Lee la documentación de Respond.js para obtener más detalles.

1.7.3.3. Respond.js y @import

Respond.js no funciona con el código CSS importado mediante la directiva @import, lo que es común en algunas configuraciones de Drupal. Lee la documentación de Respond.js para obtener más detalles.

1.7.3.4. Internet Explorer 8 y el modelo de cajas

Internet Explorer 8 no soporta completamente la propiedad box-sizing: border-box; cuando se combina con min-widthmax-widthmin-height o max-height. Por ese motivo, a partir de la versión 3.0.1 Bootstrap ya no utiliza la propiedad max-width en los elementos .containers.

1.7.4. Comentarios sobre la compatibilidad con Internet Explorer

Bootstrap no funciona con los modos de compatibilidad antiguos de Internet Explorer. Para asegurarte de que utilizas el modo correcto, puedes añadir la siguiente etiqueta <meta> en todas tus páginas, ya que activa el modo más avanzado que esté disponible en tu navegador Internet Explorer:

<meta http-equiv="X-UA-Compatible" content="IE=edge">

Confirma que Internet Explorer está utilizando el modo correcto pulsando la tecla F12 y comprobando el valor de la opción “Document Mode”.

Esta etiqueta HTML se incluye en toda la documentación y todos los ejemplos de Bootstrap para que se muestren lo mejor posible en cada versión de Internet Explorer.

Puedes obtener más información sobre este tema en la siguiente pregunta de StackOverflow.

1.7.5. Windows Phone 8 y Internet Explorer 10

Internet Explorer 10 no distingue entre la anchura del dispositivo y la anchura del viewport, por lo que no aplica correctamente las media queries del CSS de Bootstrap. Normalmente la solución es tan sencilla como añadir la siguiente regla CSS:

@-ms-viewport       { width: device-width; }

Lamentablemente esta solución no funciona con algunas versiones de Windows Phone 8. Por eso es necesario utilizar el siguiente código CSS y JavaScript:

@-webkit-viewport   { width: device-width; }
@-moz-viewport      { width: device-width; }
@-ms-viewport       { width: device-width; }
@-o-viewport        { width: device-width; }
@viewport           { width: device-width; }
if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
    var msViewportStyle = document.createElement("style")

    msViewportStyle.appendChild(
        document.createTextNode(
            "@-ms-viewport{width:auto!important}"
        )
    )

    document.getElementsByTagName("head")[0].appendChild(msViewportStyle)
}

1.7.6. Errores de redondeo de Safari

El motor de las versiones más recientes de Safari para Mac OS X tiene un error de redondeo que afecta a las clases .col-*-1 utilizadas en los diseños basados en rejilla. La consecuencia es que si tienes una fila con 12 columnas individuales, verás que su anchura total se queda un poco corta comparada con las otras filas. Hasta que Apple no solucione este problema, lo que puedes hacer es:

  • Añadir la clase .pull-right a la última columna de la fila para que se vea alineada con las columnas de las otras filas.
  • Ajustar a mano los porcentajes para que Safari no cometa el error al redondear su valor (obviamente esta solución es mucho más difícil que la anterior).

1.7.7. Elementos modales, barras de navegación y teclados virtuales

Los dispositivos Android y iOS tienen un soporte bastante limitado para la propiedad overflow: hidden aplicada sobre el elemento <body>. Así que si haces scroll por arriba o por debajo del elemento modal, verás que el resto de contenidos del <body> también hacen scroll.

Por otra parte, si utilizas elementos <input> en tu elemento modal, los dispositivos iOS tienen un error que impide actualizar la posición de esos elementos cuando se muestra el teclado virtual. Como soluciones puedes aplicar el estilo position: absolute a esos elementos o puedes programar un evento para corregir la posición del elemento a mano cuando se muestre el teclado. En cualquier caso, Bootstrap no hace nada por solucionar estos errores y por tanto, es tu responsabilidad elegir la mejor solución.

Por último, la propiedad .dropdown-backdrop no se utiliza en el elemento <nav> en iOS debido a la complejidad para superponer correctamente las capas. Por tanto, para cerrar los elementos desplegables en las barras de navegación es necesario pinchar directamente en el elemento desplegable.

1.7.8.  Los problemas con el zoom del navegador

Cuando haces zoom en una página, es normal que se produzcan pequeños fallos en algunos componentes, tanto en Bootstrap como en cualquier otra librería o framework CSS. En principio no es posible corregir ninguno de estos errores, ya que depende más de los propios navegadores de los dispositivos.

1.7.9. Los estilos para impresora

El soporte para imprimir páginas es bastante mejorable incluso en los navegadores más modernos. Google Chrome por ejemplo ignora los márgenes y utiliza su propio viewport al imprimir las páginas. Esto puede hacer que se active la rejilla ultra-pequeña de Bootstrap al imprimir una página. Para evitar estos problemas:

  • Haz que tu página se vea bien con la rejilla ultra-pequeña.
  • Modifica el valor de las variables @screen-* de Less para que el papel de tu impresora se considere más grande que el tamaño ultra-pequeño.
  • Añade media queries propias para modificar los puntos de ruptura de la rejilla solamente para el medioprint.

1.7.10.  El navegador por defecto de Android

EL navegador por defecto de Android 4.1 y de algunas versiones más recientes es diferente a Google Chrome. Lamentablemente, este navegador por defecto está lleno de errores y de inconsistencias en el tratamiento de CSS.

Uno de los errores más graves es que Android no muestra los controles laterales cuando un elemento<select> utiliza la propiedad border-radius o border. Para evitarlo puedes utilizar el siguiente código que muestra los elementos <select> sin estilo cuando la página se ve en un dispositivo Android:

<script>
var nua = navigator.userAgent;
var isAndroid = (nua.indexOf('Mozilla/5.0') > -1 && nua.indexOf('Android ') > -1 && nua.indexOf('AppleWebKit') > -1 && nua.indexOf('Chrome') === -1);
if (isAndroid) {
  $('select.form-control').removeClass('form-control').css('width', '100%');
}
</script>

Third party support

Bootstrap no soporta oficialmente ningún plugin o añadido desarrollado por terceros, pero sí que podemos ofrecerte algunos consejos para evitar problemas en tus proyectos

1.7.11. El modelo de cajas

Algunas aplicaciones desarrolladas por terceros, como por ejemplo Google Maps o Google Custom Search Engine, no funcionan bien con Bootstrap debido al conflicto que produce la regla * { box-sizing: border-box; }, que se utiliza para evitar que el padding se tenga en cuenta al calcular el tamaño de un elemento.

Dependiendo de tu proyecto, la solución puede consistir en redefinir esta propiedad para un elemento (opción 1) o para toda una región (opción 2).

/* Box-sizing resets
 *
 * Reset individual elements or override regions to avoid conflicts due to
 * global box model settings of Bootstrap. Two options, individual overrides and
 * region resets, are available as plain CSS and uncompiled Less formats.
 */

/* Opción 1A: redefinir el modelo de cajas de un elemento mediante CSS */
.element {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}

/* Opción 1B: redefinir el modelo de cajas de un elemento mediante los mixin de Bootstrap */
.element {
  .box-sizing(content-box);
}

/* Opción 2A: resetear el modelo de cajas de una región entera mediante CSS */
.reset-box-sizing,
.reset-box-sizing *,
.reset-box-sizing *:before,
.reset-box-sizing *:after {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}

/* Opción 2B: resetear el modelo de cajas de una región entera mediante los mixin de Bootstrap */
.reset-box-sizing {
  &,
  *,
  *:before,
  *:after {
    .box-sizing(content-box);
  }
}
.element {
  .reset-box-sizing();
}

gdfggdgdfg

Capítulo 2. Diseñando con rejilla

2.1.  Preparando la página

Antes de comenzar a diseñar el layout o estructura de contenidos de las páginas, es necesario realizar algunos preparativos importantes.

2.1.1. Se requiere el doctype de HTML5

Bootstrap utiliza algunos elementos HTML y algunas propiedades CSS que requieren el uso del doctype de HTML5. No olvides incluir este doctype en todas tus páginas con el siguiente código:

<!DOCTYPE html>
<html lang="es">
  ...
</html>

2.1.2. El móvil es lo más importante

Bootstrap 2 incluía algunas utilidades para hacer que las páginas se adaptaran a los dispositivos móviles. Bootstrap 3 se ha creado desde cero pensando en los móviles. Así que en vez de incluir algunos estilos opcionales para móviles, todo eso ya está incluido en el propio Bootstrap. Por eso nos gusta decir que para Bootstrap 3, los dispositivos móviles son lo más importante.

Para que las páginas se muestren correctamente y el zoom funcione bien en los dispositivos móviles, es importante que añadas la siguiente etiqueta dentro de la cabecera <head> de las páginas:

<meta name="viewport" content="width=device-width, initial-scale=1">

Si quieres deshabilitar el zoom en tus páginas, añade la propiedad user-scalable=no a la etiqueta anterior:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

Al añadir la propiedad user-scalable=no, los usuarios ya no podrán hacer zoom en la página y solamente podrán hacer scroll en sus contenidos. El resultado es que el comportamiento de la página se parece más al de una aplicación móvil nativa. En cualquier caso, limitar las libertades de los usuarios puede ser contraproducente y por tanto, no te recomendamos que utilices esta opción en todos tus sitios.

2.1.3. Imágenes responsive

Bootstrap 3 ya no adapta el tamaño de las imágenes automáticamente como sucedía en Bootstrap 2. Para mantener el mismo comportamiento de antes, debes añadir la clase .img-responsive a cada imagen que quieras que se comporte de manera responsive. Esta clase incluye las propiedades max-width: 100%; y height: auto; para que la imagen escale en función del tamaño del elemento en el que se encuentra.

<img src="..." class="img-responsive" alt="Imagen responsive">

2.1.4. Tipografía y enlaces

Bootstrap establece una serie de estilos por defecto para la tipografía de todos los elementos y para los enlaces de la página. En concreto:

  • Se establece a blanco el color de fondo del body con la propiedad background-color: white;
  • Se utiliza el valor de las variables @font-family-base@font-size-base y @line-height-base definidas por LESS como atributos de las propiedades tipográficas de los elementos.
  • Se establece el color de los enlaces al valor de la variable @link-color de LESS y sólo se muestran los enlaces subrayados en el estado :hover

Esta primera inicialización de estilos se define en el archivo scaffolding.less.

2.1.5. Normalización de estilos

Para homogeneizar los estilos iniciales en los diferentes navegadores, Bootstrap utiliza la hoja de estilos Normalize, que es un proyecto creado por Nicolas Gallagher y Jonathan Neal.

2.1.6. Centrando los contenidos de la página

Si quieres centrar una página respecto a la ventana del navegador, encierra sus contenidos dentro de un elemento y aplícale la clase .container:

<div class="container">
  ...
</div>

La anchura del contenedor varía en cada punto de ruptura del diseño para adaptarse a la rejilla. Los contenedores no se pueden anidar debido a la propiedad padding y a su anchura fija.

2.2. Tipos de rejillas

Bootstrap incluye una rejila o retícula fluída pensada para móviles y que cumple con el diseño web responsive. Esta retícula crece hasta 12 columnas a medida que crece el tamaño de la pantalla del dispositivo. Bootstrap incluye clases CSS para utilizar la rejilla directamente en tus diseños y también define mixins de LESS para que puedas crear diseños más semánticos.

2.2.1. Introducción

El diseño de páginas basado en rejilla se realiza mediante filas y columnas donde se colocan los contenidos. Así funciona la rejilla de Bootstrap:

  • Las filas siempre se definen dentro de un contenedor de tipo .container (anchura fija) o de tipo.container-fluid (anchura variable). De esta forma las filas se alinean bien y muestran el padding correcto.
  • Las filas se utilizan para agrupar horizontalmente a varias columnas.
  • El contenido siempre se coloca dentro de las columnas, ya que las filas sólo deberían contener como hijos elementos de tipo columna.
  • Bootstrap define muchas clases CSS (como por ejemplo .row y .col-xs-4) para crear rejillas rápidamente. También existen mixins de Less para crear diseños más semánticos.
  • La separación entre columnas se realiza aplicando padding. Para contrarrestar sus efectos en la primera y última columnas, las fileas (elementos .row) aplican márgenes negativos.
  • Las columnas de la rejilla definen su anchura especificando cuántas de las 12 columnas de la fila ocupan. Si por ejemplo quieres dividir una fila en tres columnas iguales, utilizarías la clase .col-xs-4 (el4 indica que cada columna ocupa 4 de las 12 columnas en las que se divide cada fila).

NOTA Si quieres crear un diseño totalmente fluido que ocupe toda la anchura del navegador, deberías encerrar las rejillas dentro de un elemento al que apliques los estilos padding: 0 15px;. De esta forma se pueden neutralizar los márgenes margin: 0 -15px; que se aplican a los elementos .row.

2.2.2.  Media queries

Bootstrap utiliza las siguientes media queries para establecer los diferentes puntos de ruptura en los que la rejilla se transforma para adaptarse a cada dispositivo.

/* Dispositivos muy pequeños (teléfonos de hasta 768px de anchura) */
/* No se define ninguna media query porque este es el estilo por
   defecto utilizado por Bootstrap 3 */

/* Dispositivos pequeños (tablets, anchura mayor o igual a 768px) */
@media (min-width: @screen-sm-min) { ... }

/* Dispositivos medianos (ordenadores, anchura mayor o igual a 992px) */
@media (min-width: @screen-md-min) { ... }

/* Dispositivos grandes (ordenadores, anchura mayor o igual a 1200px) */
@media (min-width: @screen-lg-min) { ... }

En ocasiones, también se utilizan las siguientes media queries que definen la propiedad max-width y permiten restringir los dispositivos a los que se aplican los estilos CSS:

@media (max-width: @screen-xs-max) { ... }
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { ... }
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) { ... }
@media (min-width: @screen-lg-min) { ... }

2.2.3. Características de cada rejilla

La siguiente tabla muestra las características de la rejilla de Bootstrap en los diferentes tipos de dispositivos.

Dispositivos muy pequeñosTeléfonos (<768px) Dispositivos pequeñosTablets (≥768px) Dispositivos medianosOrdenadores (≥992px) Dispositivos grandesOrdenadores (≥1200px)
Comportamiento Las columnas se muestran siempre horizontalmente. Si se estrecha el navegador, las columnas se muestran verticalmente. A medida que aumenta su anchura, la rejilla muestra su aspecto horizontal normal.
Anchura máxima del contenedor Ninguna (auto) 728px 940px 1170px
Prefijo de las clases CSS .col-xs- .col-sm- .col-md- .col-lg-
Número de columnas 12
Anchura máxima de columna auto 60px 78px 95px
Separación entre columnas 30px (15px a cada lado de la columna)
¿Permite anidación? Si
¿Permite desplazar columnas? No Si
¿Permite reordenación de columnas? No Si

2.2.4. Ejemplo de rejilla creada con Bootstrap

El siguiente ejemplo muestra cómo crear una rejilla con las clases .col-md-*. En los dispositivos móviles (extra pequeño o pequeño) esta rejilla se muestra verticalmente, pero en un ordenador (medio o grande) se ve horizontalmente.

<div class="row">
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
  <div class="col-md-1">.col-md-1</div>
</div>
<div class="row">
  <div class="col-md-8">.col-md-8</div>
  <div class="col-md-4">.col-md-4</div>
</div>
<div class="row">
  <div class="col-md-4">.col-md-4</div>
  <div class="col-md-4">.col-md-4</div>
  <div class="col-md-4">.col-md-4</div>
</div>
<div class="row">
  <div class="col-md-6">.col-md-6</div>
  <div class="col-md-6">.col-md-6</div>
</div>

Ver este ejemplo en una nueva página

2.2.5. Ejemplo de contenedor de anchura variable

Si quieres transformar una rejilla de anchura fija en una rejilla de anchura variable que ocupa toda la anchura del navegador, reemplaza la clase CSS .container por .container-fluid en el elemento que encierra a todos los demás elementos de la rejilla:

<div class="container-fluid">
  <div class="row">
    ...
  </div>
</div>

2.2.6. Ejemplo de rejilla para móviles y ordenadores

Si no quieres que las columnas de la rejilla se muestren verticalmente en los dispositivos pequeños, utiliza a la vez las clases .col-xs-* y .col-md-*, tal y como muestra el siguiente ejemplo.

<!-- En los móviles las columnas se muestran verticalmente porque
     una de ellas ocupa toda la anchura del dispositivo y la otra
     columna ocupa la mitad -->
<div class="row">
  <div class="col-xs-12 col-md-8">.col-xs-12 col-md-8</div>
  <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>

<!-- En un móvil las columnas ocupan la mitad del dispositivo y en un 
     ordenador ocupan la tercera parte de la anchura disponible -->
<div class="row">
  <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
  <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
  <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>

<!-- Las columnas ocupan siempre la mitad de la pantalla, tanto en un
     móvil como en un ordenador de escritorio -->
<div class="row">
  <div class="col-xs-6">.col-xs-6</div>
  <div class="col-xs-6">.col-xs-6</div>
</div>

Ver este ejemplo en una nueva página

2.2.7. Ejemplo de rejilla para móviles, tablets y ordenadores

A partir del ejemplo anterior, puedes hacer que el layout sea todavía más dinámico añadiendo las clases.col-sm-* pensadas para tablets:

<div class="row">
  <div class="col-xs-12 col-sm-6 col-md-8">.col-xs-12 .col-sm-6 .col-md-8</div>
  <div class="col-xs-6 col-md-4">.col-xs-6 .col-md-4</div>
</div>

<div class="row">
  <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>
  <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>

  <!-- Código opcional para limpiar las columnas XS en caso de que el
       contenido de todas las columnas no coincida en altura -->
  <div class="clearfix visible-xs"></div>
  <div class="col-xs-6 col-sm-4">.col-xs-6 .col-sm-4</div>
</div>

Ver este ejemplo en una nueva página

3.1. Titulares

Bootstrap 3 define estilos por defecto para todos los niveles de titulares de las páginas, desde <h1> hasta<h6>. Ejemplo:

<h1>h1. Bootstrap heading</h1>
<h2>h2. Bootstrap heading</h2>
<h3>h3. Bootstrap heading</h3>
<h4>h4. Bootstrap heading</h4>
<h5>h5. Bootstrap heading</h5>
<h6>h6. Bootstrap heading</h6>

Así se ve este ejemplo en tu navegador:

A continuación se muestra una imagen del aspecto que debería tener este ejemplo, para que puedas comparar los dos:

Titulares por defecto en Bootstrap 3Figura 3.1 Titulares por defecto en Bootstrap 3

Bootstrap también define estilos especiales para los elementos <small> incluidos dentro de un titular y utilizados habitualmente para mostrar información secundaria. Ejemplo:

<h1>h1. Bootstrap heading <small>Secondary text</small></h1>
<h2>h2. Bootstrap heading <small>Secondary text</small></h2>
<h3>h3. Bootstrap heading <small>Secondary text</small></h3>
<h4>h4. Bootstrap heading <small>Secondary text</small></h4>
<h5>h5. Bootstrap heading <small>Secondary text</small></h5>
<h6>h6. Bootstrap heading <small>Secondary text</small></h6>

Así se ve este ejemplo en tu navegador:

Y esta es la imagen del aspecto que debería tener este ejemplo:

Titulares con elementos secundarios en Bootstrap 3Figura 3.2 Titulares con elementos secundarios en Bootstrap 3

3.2. Texto

El tamaño de letra (font-size) por defecto de Bootstrap 3 es 14px y el interlineado (line-height) es 1.428. Estos valores se aplican tanto al <body> como a todos los párrafos. Estos últimos también incluyen un margen inferior cuyo valor es la mitad que su interlineado (10px por defecto).

Ejemplo:

<p>Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula.</p>

<p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit. Donec ullamcorper nulla non metus auctor fringilla.</p>

<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p>

Así se ve este ejemplo en tu navegador:

Y esta es la imagen del aspecto que debería tener este ejemplo:

Texto normal en Bootstrap 3Figura 3.3 Texto normal en Bootstrap 3

3.2.1. Texto destacado

Para destacar un párrafo sobre los demás, añade la clase .lead. Ejemplo:

<p class="lead">Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Duis mollis, est non commodo luctus.</p>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Texto destacado en Bootstrap 3Figura 3.4 Texto destacado en Bootstrap 3

3.2.2. Variables de LESS

La escala tipográfica de tamaños de letra se basa en dos variables LESS definidas en el archivo variables.less@font-size-base y @line-height-base.

La primera variable es el tamaño de letra base y la segunda es el interlineado base del texto. Como estos valores se utilizan para calcular los márgenes, rellenos e interlineados de todos los elementos, si modificas sus valores Bootstrap adaptará automáticamente todo el diseño.

4.1.  Tablas

4.1.1.  Tablas básicas

Añade la clase .table a cualquier elemento <table> para aplicar los estilos básicos de Bootstrap 3 para tablas. El resultado es una tabla con un padding muy sutil y con líneas de separación solamente en las filas.

Puede parecer absurdo tener que añadir la clase .table para que se apliquen los estilos a las tablas, pero ten en cuenta que el elemento <table> se utiliza para muchas otras cosas que no son necesariamente tablas, como por ejemplo calendarios y selectores de fechas.

Ejemplo:

<table class="table">
  ...
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla básica en Bootstrap 3Figura 4.1 Tabla básica en Bootstrap 3

4.1.2. Tablas cebreadas

Las tablas cebreadas son aquellas cuyas filas alternan su color de fondo para mejorar la legibilidad de los contenidos. Su aspecto recuerda a la piel de una cebra y de ahí su nombre. En inglés se denominan “striped tables” y por eso en Bootstrap 3 estas tablas se crean añadiendo la clase .table-striped. Ejemplo:

<table class="table table-striped">
  ...
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla cebreada en Bootstrap 3Figura 4.2 Tabla cebreada en Bootstrap 3

ADVERTENCIA Como las tablas cebreadas utilizan el selector :nth-child de CSS, no funcionan en Internet Explorer 8.

4.1.3. Tablas con bordes

Si prefieres utilizar el estilo tradicional de las tablas con los cuatro bordes en todas las celdas y en la propia tabla, añade la clase .table-bordered. Ejemplo:

<table class="table table-bordered">
  ...
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla con bordes en Bootstrap 3Figura 4.3 Tabla con bordes en Bootstrap 3

4.1.4. Tablas dinámicas

Para que los contenidos de la tabla sean todavía más fáciles de entender, añade la clase .table-hover para modificar ligeramente el aspecto de las filas cuando el usuario pasa el ratón por encima de ellas (sólo funciona para las filas dentro de <tbody>).

Ejemplo:

<table class="table table-hover">
  ...
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla dinámica en Bootstrap 3Figura 4.4 Tabla dinámica en Bootstrap 3

4.1.5. Tablas condensadas

Cuando una tabla es muy grande o cuando tienes muchas tablas en una misma página, puede ser interesante mostrar sus contenidos de forma más compacta. Añade la clase .table-condensed a tus tablas y el padding se reducirá a la mitad. Ejemplo:

<table class="table table-condensed">
  ...
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla condensada en Bootstrap 3Figura 4.5 Tabla condensada en Bootstrap 3

4.1.6. Tablas semánticas

Las clases CSS semánticas explicadas en el capítulo anterior para el texto también se pueden aplicar a las filas y a las celdas de una tabla:

  • .active, aplica el color del estado hover a la fila o celda para que parezca que está activa.
  • .success, indica que el resultado de alguna operación ha sido un éxito.
  • .warning, avisa al usuario que se ha producido alguna circunstancia que puede requerir su atención.
  • .danger, indica que una acción es negativa o potencialmente peligrosa.

Ejemplo:

<table>
<tbody>
  <!-- Aplicadas en las filas -->
  <tr class="active">...</tr>
  <tr class="success">...</tr>
  <tr class="warning">...</tr>
  <tr class="danger">...</tr>

  <!-- Aplicadas en las celdas (<td> o <th>) -->
  <tr>
    <td class="active">...</td>
    <td class="success">...</td>
    <td class="warning">...</td>
    <td class="danger">...</td>
  </tr>
</tbody>
</table>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla semántica en Bootstrap 3Figura 4.6 Tabla semántica en Bootstrap 3

4.1.7. Tablas responsive

La solución que propone Bootstrap 3 para crear tablas responsive que se vean bien en dispositivos pequeños consiste en añadir un scroll horizontal a las tablas que sean demasiado anchas. Para ello, encierra cualquier tabla con la clase .table dentro de un elemento con la clase .table-responsive. Cuando las tablas responsive se muestran en dispositivos con una anchura superior a 768px, se ven igual que cualquier otra tabla normal.

Ejemplo:

<div class="table-responsive">
  <table class="table">
    ...
  </table>
</div>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Tabla responsive en Bootstrap 3Figura 4.7 Tabla responsive en Bootstrap 3

4.2.  Imágenes

Bootstrap 3 define varias clases CSS para decorar las imágenes de tus sitios web:

  • .img-rounded, añade unas pequeñas esquinas redondeadas en todos los lados de la imagen aplicando el estilo border-radius: 6px.
  • .img-thumbnail, muestra la imagen con un relleno blanco y un borde fino simulando el aspecto de las fotografías de las antiguas cámaras instantáneas. Añade además una breve animación para hacer que la imagen aparezca al cargar la página.
  • .img-circle, convierte la imagen en un círculo aplicando el estilo border-radius: 50%

ADVERTENCIA El navegador Internet Explorer 8 no soporta el uso de esquinas redondeadas, por lo que los estilos .img-rounded y .img-circle no tienen ningún efecto sobre las imágenes.

Ejemplo:

<img src="..." alt="..." class="img-rounded">
<img src="..." alt="..." class="img-circle">
<img src="..." alt="..." class="img-thumbnail">

Y esta es la imagen del aspecto que debería tener este ejemplo:

Imágenes decoradas en Bootstrap 3Figura 4.8 Imágenes decoradas en Bootstrap 3

Si estás buscando cómo hacer que las imágenes de Bootstrap 3 se comporten de manera responsive como en Bootstrap 2, consulta la sección de imágenes responsive de los capítulos anteriores.

4.3. Utilidades

4.3.1. Icono para cerrar

Bootstrap 3 define la clase .close para mostrar la entidad HTML &times; como si fuera la típica X asociada con el cierre de una ventana o aplicación. Utilízalo para mostrar el icono de cerrar en las ventanas modales o en las alertas sin tener que utilizar una imagen.

Ejemplo:

<button type="button" class="close" aria-hidden="true">&times;</button>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Icono de cierre en Bootstrap 3Figura 4.9 Icono de cierre en Bootstrap 3

4.3.2. Elementos flotantes

Flotar un elemento a la derecha o a la izquierda es muy habitual en la mayoría de diseños web. Por eso Bootstrap 3 define dos clases CSS genéricas llamadas .pull-left y .pull-right que puedes aplicar sobre cualquier elemento:

Ejemplo:

<div class="pull-left">...</div>
<div class="pull-right">...</div>

Este es el código CSS aplicado a cada clase (la palabra reservada !important se utiliza para evitar posibles problemas con la especificidad de los selectores):

.pull-left {
  float: left !important;
}
.pull-right {
  float: right !important;
}

Bootstrap 3 también define mixins para que puedas utilizar estos estilos en tus archivos LESS:

.elemento {
  .pull-left();
}
.otro-elemento {
  .pull-right();
}

No utilices estas clases para alinear los componentes de las barras de navegación .navbar. Utiliza en su lugar las dos clases equivalentes .navbar-left y .navbar-right.

4.3.3. Elementos centrados

Aplica la clase especial center-block para centrar horizontalmente cualquier elemento (el elemento centrado se convierte en un elemento de bloque):

Ejemplo:

<div class="center-block">...</div>

Este es el código CSS aplicado a esta clase:

.center-block {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

Bootstrap 3 también define un mixin para que puedas utilizar estos estilos en tus archivos LESS:

.elemento {
  .center-block();
}

4.3.4. Limpiando floats

Cuando un diseño utiliza muchos elementos flotantes, es común tener que limpiar un elemento para que no le afecten otros elementos flotantes. Bootstrap 3 utiliza para ello el famoso hack clearfix creado originalmente por Nicolas Gallagher.

Ejemplo:

<div class="clearfix">...</div>

Este es el código CSS aplicado a esta clase:

.clearfix:before,
.clearfix:after {
  display: table;
  content: " "
}
.clearfix:after {
  clear: both;
}

Bootstrap 3 también define un mixin para que puedas utilizar estos estilos en tus archivos LESS:

.elemento {
  .clearfix();
}

4.3.5.  Ocultando y mostrando elementos

Otras de las utilidades incluidas por Bootstrap 3 son las clases .show y .hide, que muestran y ocultan cualquier elemento.

Ejemplo:

<div class="show">...</div>
<div class="hide">...</div>

Este es el código CSS aplicado a estas clases (de nuevo se utiliza !important para evitar problemas con los selectores):

.show {
  display: block !important;
}
.hide {
  display: none !important;
}

Bootstrap 3 también define dos mixins para que puedas utilizar estos estilos en tus archivos LESS:

.elemento {
  .show();
}
.otro-elemento {
  .hide();
}

4.3.6.  Contenidos ocultos

Cuando se diseña un sitio web accesible, es común añadir ayudas en forma de texto que no se ve por pantalla, pero sí que se lee en los lectores que utilizan para navegar las personas discapacitadas.

Bootstrap 3 define la clase .sr-only para marcar un contenido como oculto y que sólo esté disponible para los lectores (“screen readers” en inglés, de ahí el nombre de la clase CSS). Ejemplo:

<a class="sr-only" href="#contenido">Ir al contenido</a>

Este es el código CSS aplicado a esta clase:

.sr-only {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  width: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
}

Bootstrap 3 también define un mixin para que puedas utilizar estos estilos en tus archivos LESS:

.saltar-navegacion {
  .sr-only();
}

4.3.7. Reemplazando texto por imágenes

Una de las técnicas más habituales para mostrar el logotipo de los sitios web consiste en ocultar el texto de un elemento <h1> para que se vea la imagen de fondo que contiene el logotipo. Esta técnica es tan habitual que Bootstrap 3 define la clase .text-hide para que puedas aplicarla a cualquier elemento. Ejemplo:

<h1 class="text-hide">Nombre de la empresa</h1>

Este es el código CSS aplicado a esta clase:

.text-hide {
  background-color: transparent;
  border: 0;
  color: transparent;
  font: 0/0 a;
  text-shadow: none;
}

Bootstrap 3 también define un mixin para que puedas utilizar estos estilos en tus archivos LESS:

.logotipo {
  .text-hide();
}

4.3.8. Utilidades responsive

Las utilidades responsive permite diseñar más rápidamente sitios web móviles, ya que muestran u ocultan elementos en función del tipo de dispositivo que utiliza el usuario para navegar. También se incluyen clases para mostrar/ocultar elementos al imprimir la página.

Estas clases deben utilizarse con moderación y siempre para mejorar el aspecto de los contenidos en cada tipo de dispositivo. Además, sólo funcionan para los elementos de bloque y las tablas, por lo que no podrás aplicarlos a los elementos en línea.

Utiliza alguna de estas clases o combina varias entre sí para definir cómo se ven tus contenidos en cada tipo de dispositivo (teléfono = menos de 768px; tablet = más de 768px; ordenador = más de 992px; ordenador grande = más de 1200px):

Clase Teléfonos Tablets Ordenador Ordenador grande
.visible-xs Visible Oculto Oculto Oculto
.visible-sm Oculto Visible Oculto Oculto
.visible-md Oculto Oculto Visible Oculto
.visible-lg Oculto Oculto Oculto Visible
.hidden-xs Oculto Visible Visible Visible
.hidden-sm Visible Oculto Visible Visible
.hidden-md Visible Visible Oculto Visible
.hidden-lg Visible Visible Visible Oculto

Igualmente, puedes utilizar estas clases para definir qué contenidos se muestran al imprimir la página:

Clase Navegador Impresora
.visible-print Oculto Visible
.hidden-print Visible Oculto

A continuación se muestra un ejemplo que utiliza todas estas clases. Prueba a redimensionar la ventana del navegador o accede a esta página con diferentes dispositivos para ver las diferencias:

Ver ejemplo en una página completa

6.1.  Iconos (glyphicons)

Bootstrap incluye 180 iconos creados mediante una fuente especial llamada Glyphicon Halflings. Aunque esta fuente normalmente no es gratuita, su creador permite utilizar estos iconos gratuitamente dentro de Bootstrap 3. Lo único que te pedimos es que, si es posible, incluyas en tu sitio un enlace al proyectoGlyphicons.

6.1.1. Listado completo de los iconos disponibles

A continuación se muestran todos los iconos de Bootstrap 3 y las clases CSS necesarias para utilizar cada uop:   (buscatelos majete)

6.2. Menús desplegables

Este componente permite mostrar una lista de enlaces como si fuera un menú desplegable con la lista de acciones que el usuario puede realizar. Para que funcionen sus características interactivas, es necesario utilizar también el plugin dropdown de JavaScript. Ejemplo:

<div class="dropdown">
  <button class="btn dropdown-toggle sr-only" type="button"
          id="dropdownMenu1" data-toggle="dropdown">
    Menú desplegable
    <span class="caret"></span>
  </button>

  <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
    <li role="presentation">
      <a role="menuitem" tabindex="-1" href="#">Acción</a>
    </li>
    <li role="presentation">
      <a role="menuitem" tabindex="-1" href="#">Otra acción</a>
    </li>
    <li role="presentation">
      <a role="menuitem" tabindex="-1" href="#">Otra acción más</a>
    </li>
    <li role="presentation" class="divider"></li>
    <li role="presentation">
      <a role="menuitem" tabindex="-1" href="#">Acción separada</a>
    </li>
  </ul>
</div>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Menú desplegable en Bootstrap 3Figura 6.2 Menú desplegable en Bootstrap 3

Como se muestra en el ejemplo anterior, es necesario utilizar la clase .dropdown para encerrar tanto el menú como el elemento que lo activa (en este caso, el botón cuyo id es #dropdownMenu1). También es posible utilizar tu propia clase CSS, siempre que ese elemento aplique el estilo position: relative;.

6.2.1. Opciones de alineación

Añade la clase .pull-right a la lista .dropdown-menu que define el menú para alinear sus contenidos a la derecha.

<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dLabel">
  ...
</ul>

6.2.2. Títulos de sección

Los menús desplegables también pueden contener elementos con la clase .dropdown-header para definir el título de un grupo de enlaces. Ejemplo:

<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu2">
  <li role="presentation" class="dropdown-header">Título de sección #1</li>
  ...
  <li role="presentation" class="divider"></li>
  <li role="presentation" class="dropdown-header">Título de sección #2</li>
  ...
</ul>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Menú desplegable con títulos de sección en Bootstrap 3Figura 6.3 Menú desplegable con títulos de sección en Bootstrap 3

6.2.3. Deshabilitando elementos del menú

Si se añade la clase .disabled al elemento <li> de un enlace del menú, este se muestra deshabilitado, por lo que el usuario puede ver que esa acción existe, pero que no es posible utilizarla en ese momento. Ejemplo:

<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu3">
  <li role="presentation">
    <a role="menuitem" tabindex="-1" href="#">Enlace normal</a>
  </li>
  <li role="presentation" class="disabled">
    <a role="menuitem" tabindex="-1" href="#">Enlace deshabilitado</a>
  </li>
  <li role="presentation">
    <a role="menuitem" tabindex="-1" href="#">Otro enlace normal</a>
  </li>
</ul>

Así se ve este ejemplo en tu navegador:

Y esta es la imagen del aspecto que debería tener este ejemplo:

Menú desplegable con enlaces deshabilitados en Bootstrap 3Figura 6.4 Menú desplegable con enlaces deshabilitados en Bootstrap 3

5.1. Formulario básico

Bootstrap 3 aplica por defecto algunos estilos a todos los componentes de los formularios. Si además añades la clase .form-control a los elementos <input><textarea> y <select>, su anchura se establece awidth: 100%. Para optimizar el espaciado, utiliza la clase .form-group para encerrar cada campo de formulario con su <label>.

Ejemplo:

<form role="form">
  <div class="form-group">
    <label for="ejemplo_email_1">Email</label>
    <input type="email" class="form-control" id="ejemplo_email_1"
           placeholder="Introduce tu email">
  </div>
  <div class="form-group">
    <label for="ejemplo_password_1">Contraseña</label>
    <input type="password" class="form-control" id="ejemplo_password_1" 
           placeholder="Contraseña">
  </div>
  <div class="form-group">
    <label for="ejemplo_archivo_1">Adjuntar un archivo</label>
    <input type="file" id="ejemplo_archivo_1">
    <p class="help-block">Ejemplo de texto de ayuda.</p>
  </div>
  <div class="checkbox">
    <label>
      <input type="checkbox"> Activa esta casilla
    </label>
  </div>
  <button type="submit" class="btn btn-default">Enviar</button>
</form>

Y esta es la imagen del aspecto que debería tener este ejemplo:

Formulario básico en Bootstrap 3