The beauty of Directives, Transclusion in Angular.JS


To be honest in Angular directives are nothing but DOM elements simply put on steroids. Now if you add transclusion to it literally the possibilty of exploring the playground is boundless .With that said before diving into what directives are and how cool are they, let us basically understand what makes directives in angular so powerful.

Some of the well known directives which are used regularly are :

  • ng-src
  • ng-show
  • ng-hide
  • ng-model
  • ng-repeat

Custom Directives and the benifit of creating reusable components

We often tend to waste a lot of time and energy in writing code which is already/previously written and components which are already built. But what if you could write them only once and reuse them as many times as possible ?
ANS : “Directives”
You can create truly reusable components with directives, and the approach to build custom components is definitely neater and more intuitive.
Where do Custom Directives get implemented ?

  • elements
  • attributes
  • classes

Scope of Directives

After we initialize a directive we can observe that it gets a parent scope by default. In the best interests of the application you write you won’t really want that to happen. So in order to freely modify the properties of the directive we expose the parent’s controller scope to the directives.In some cases your directive may want to add several properties and functions to the scope that are for internal use only.

Example : If you have a directive that deals with comments(just like my sTeam web interface), you may want to set some internal variable to show or hide some of its specific sections. If we set these models to the parent’s scope we would pollute it.

Without polluting, is there any option ?

Absolutely there are two options,

  • Use a child scope
  • Use an isolated scope

If we use a child scope then the child scope prototypically inherits the parent’s scope. If we use an isolated scope then its all on its own, by not inheriting its parent’s scope


Transclusion is a feature that enables us to wrap a directive around arbitrary content. We can extract and compile it against the correct scope later, and eventually place it at the specified position in the directive template. If you set transclude:true in the directive definition, a new transcluded scope will be created which prototypically inherits from the parent scope. If you want your directive with isolated scope to contain an arbitrary piece of content and execute it against the parent scope, transclusion can be used.

Example :


See the above snippet, here ng-transclude says where to put the transcluded content. In this case the DOM content Hello {{name}} is extracted and put inside div ng-tran- sclude /div> . The important point to remember is that the expression {{name}} interpolates against the property defined in the parent scope rather than the isolated scope.

Thats it folks,
Happy Hacking !!

Continue Reading The beauty of Directives, Transclusion in Angular.JS

Sending a streaming zip file in node.js

The webapp generator that we set up is now up and running on a heroku instance and is working to generate zips of generated websites.

One of the requirements was that when the front-end presses, the “Generate” button, a request is created to the backend node app to start creating the html pages, and pack them into a zip file. Then the said zip file will be made available to download.

I intended for the zip file to become available to download immediately and the server to continuously pack data into the zip, as the user downloads it – a process known as streaming zip serving. (If you might have noticed, Google Drive uses this too. You do not know the final zip size before the download has completed.)

If the server takes time in creating the contents of the zip, this method can help, as it will allow the user to start downloading the file, before all the contents are finalised.


To achieve the mentioned goal, the idea Node.js module to use archiver.

If you look at the app.js code you’ll find on the /generate POST endpoint, we take the ‘req’ params and pass to a pipeToRes() function, which is defined in generator.js'/generate', uploadedFiles, function(req, res, next) {
res.setHeader('Content-Type', 'application/zip');
generator.pipeZipToRes(req, res);

And in the generator, you can find this block of code that pipes the streaming archive to the response.


      const zipfile = archiver('zip');

      zipfile.on('error', (err) => {
        throw err;

      zipfile.pipe(res);, '/').finalize();

We zip the directory defined in distPath and the archiver API has a fluent call to .finalize() that finished the download for the user, when the zip is completed.

Continue Reading Sending a streaming zip file in node.js

Language Localization for Engelsystem

Localization takes place when you adapt content to a specific location. In translation, it means that your content can be read by another in their native language with as much ease as if the information were written in that language to begin with. In other words, just translating a document does not mean that it has been localized. When it is your brand, information, or material at stake, it is important to understand the elements that separate a mere translation from the language localization necessary to maximize impact with your target audience.

Some Examples of Localized Content

Understanding excellent localization may be easier with some positive examples.

  • Google is the search engine of choice in much of the English-speaking world. It accounts for only one percent of the search market in China, however. Baidu gives Chinese users the look and feel that they understand. Has Google given up? Hardly, they are working on improving the localization of their content.
  • Many television and phone manufacturers compete for the French markets. Samsung is made in Korea, but they marketed themselves according to the French tastes and desires. The manufacturer stressed the artistic design and high definition of its televisions. Their phones were tailored to the French market with apps specifically designed for the French lifestyle and location of the users.
  • Packaging is very important to an American consumer, even with toiletries; it is not very important at all to Chinese consumers. Proctor and Gamble localized their product marketing for Crest toothpaste by reducing their packaging costs and focusing on the flavors, which the Chinese do value. The localization not only increased their market share but cut their costs by 50 percent.

Remember, the purpose of localization is to give your text or product the impression that they were designed or written specifically for that market regardless of the language, culture, or location.

Understanding the Localization Process in Translation

Within the world of translation, localization includes a number of critical elements such as:

  • Modification of grammar, punctuation, and lexical elements
  • Adaptation of formality and other culturally-based items of etiquette
  • Ensuring figurative language is replaced with localized idioms and metaphors
  • Making sure content is culturally sensitive and relevant
  • Aligning conversions such as money, time, dates, voltages, and so forth
  • Formatting numbers, values, and so on according to local variations
  • Verifying the text and format meet legal requirements

In order to localize content, the translator must have a comprehensive understanding of the culture, the language, and the specific field the information references. Note, in the list above, those cultural adaptations are as important to the translation as lexical and grammatical issues. Local sensitivities must be considered to avoid conflicts with culture, customs, or habits.

Some specific items that might be tailored to the local populace are references to political leaders or situations that are deemed offensive (think about China and Taiwan), animals (rats are looked upon favorably in china as is the chicken), flowers, gestures, shoes (wearing shoes inside can be taboo), gum (illegal in Singapore), meats (think about Kosher or Halal ideals, vegetarianism is also stressed in some places), smoking (banned for Sikhs), and bodily functions (what is funny or cute for some is offensive to others).

If your documents, texts, or project will be going to several locations and translated into several local languages, you might want to consider “internationalizing” it first. This is the process of highlighting or adjusting any questionable text or ideas in advance to save the translator’s time when preparing it for the local dialect. The process is most effective when done during the formulation stages of the document, but it can be done later as well. For papers that are going to be translated into multiple languages, the time and expense that could be saved through this step are tremendous.

Whether your documents go through an intermediary step or are translated straight to a local language, it is imperative that your translation service understands the importance of an exhaustive and thorough localization process.


When selecting translation services, one of the most important questions you can ask is whether or not you want your documents prepared for internationalization or localization.

Localization is the process of translating a document or project for use within a specific target market. Localization includes all of the components of translation required to meet cultural and linguistic adaptations of text. Localization can be accomplished from an original source text, or from a test that has been prepared for international translation.

Internationalization is the process of preparing a document or project to be used anywhere in the world and localized as necessary. The process of creating a document that is bothlinguistically deliverable and culturally sensitive for a worldwide audience is not easy or quick. Yet, when internationalization takes place during the initial stages of product development it is far easier than trying to work backwards, and a document that has been successfully internationalized can be quickly and easily localized.

Understanding the Elements

Some of the elements that are impacted during the internationalization or localization process include:

  • Formatting: Not everybody formats their date the same way. Time signatures are also not universal. Even numbers are not standard. Deciding where to put colons, commas, symbols and the like will make a huge difference in the readability of your final document.
  • Symbols/Abbreviations: English speakers often do not realize that their symbols are not universal. When symbols are used in text, they can take on many different meanings based upon the target language. Other times, the symbol may have no meaning at all, in which case the intent of the content will have been lost without a proper translation. Consider such a simple abbreviation as E. for east: since the German word for east is osten, east would be O. Even if we agreed to use metric vs imperial or American units, a square kilometer (sq km) would be a German Quadratkilometer or qkm.
  • Gender: English does not assign gender to most items, Spanish and French assign gender to just about everything, Japanese has gender, but also special rules for adults and children.
  • Graphics:Items that are just fine in one culture may be meaningless in another culture, or, worse, offensive and derogatory. For example, the stork delivers babies in many cultures; however, in Japan, a stork and baby will not have the same meaning. Colors also have different meanings in different cultures. Not everybody associates white with purity, weddings, and truth.
  • Legal Requirements: who has to sign documents, where do signatures go, are witnesses required, who is allowed to be a witness, and so forth. If you are doing business in an international community, these are essential elements of translation.

Why Internationalization or Localization is so Important

If your document is going to go to one region or local, and only one region or local, direct translation into the target text is the cheapest and most expedient way to translate your document. However, suppose you have a document that is going to be translated into 10, 20, even 50 different languages.

While you could hire a unique translator for each language independent of all others, another approach would be to go through the internationalization process. During this process, translators would create a master document that addresses the main elements that would be difficult to translate. They would standardize the elements that could be standardized, and highlight those ideas which are going to need more elaboration by a local translator. This way, when the translators who will be working on the localized documents start their translations, much of the groundwork has already been done for them.

When choosing a translation service, you will want to know the number of target languages. If you only have one or two, you may be better off going with a straight translation; however, if you are going to be addressing multiple languages (even many within the same family: Mexican Spanish, Puerto Rican Spanish, Argentinian Spanish, Andalusian Spanish) you may be able to save considerable time.

During the 3rd week of my Summer of Code, I’ve implemented localization on Engelsystem. Users can get more information on my previous blog post, here.

In the later weeks, other developers and I would be working on adding more languages to the system. Anyone who would like to work in the project are welcome. Developers can feel free to take up any issues they would like to work on just like any other open source projects.

Development:                                           Issues/Bugs:

Continue Reading Language Localization for Engelsystem

Language Localization on Engelsystem

During the 3rd  week of my Summer of Code, I worked on implementing Localization of different languages on the system and creating a different settings page for user and admin.

One of our goals is for Engelsystem to be a great experience for users worldwide. To achieve this goal we need to make some significant changes. The main reason of localization is that it can be used by different people with different native languages across the world and help them to understand the system better .

First, a little background.

Right now, Engelsystem has two localization systems available:

  • Deutsch
  • English

All of the current translations will be updated. We are also expanding the list of supported languages to as many as we can. Currently, I’ve implemented:

  • Bulgarian
  • Chinese- simplified
  • English UK
  • French
  • Hindi-IN
  • Hungarian
  • Irish
  • Punjabi-IN
  • Spanish
  • Tamil-IN

The tools used in implementing localization are poedit where we create the .po files .

For installing Poedit on Linux distro’s, users can use the following command in the terminal,

sudo apt-get install poedit

For Windows and Mac users, they can download the executable file here.

Steps to run Poedit in Linux/Windows/Mac:

  1. choose File → New Catalog.Enter the details in Translation properties like your email. Source code charset should be selected as UTF-8.Now the source path should be selected as (.) dot to tell Poedit to search for PHP files in the same folder.
  2. In the source, there are three options ‘_’ , ‘gettext’ and  ‘gettext_noop’.Make sure all necessary functions are added and press OK.
  3. Now we can see the list of strings on left and their translated strings on the right.we need to install the necessary dictionary for this.
  4. Save the file  with an extension ‘.po’ . 

    In Engelsystem, to implement the translated localization file we need to make the changes in the file,internationalization_helper.php

    $locales = array(
    ‘de_DE.UTF-8’ => “Deutsch”,
    ‘hi_IN.UTF-8’ => “Hindi”,
    ‘sp_EU.UTF-8’ => “Spanish”,
    ‘en_US.UTF-8’ => “English”

    During the week 3 of my Summer of Code I also worked on creating different settings page for user and Admin. Earlier settings page for user and admin were same, providing same privileges to both of them. Now we have separate pages for both.

                                                                             User Settings
    Admin Settings
                                                                        Admin Settings

    In the later weeks, other developers and I would be working on adding more languages to the system. Anyone who would like to work in the project are welcome. Developers can feel free to take up any issues they would like to work on just like any other open source projects.

    Development:                                           Issues/Bugs:

Continue Reading Language Localization on Engelsystem

Why should you use Heroku?

Last week I’ve dedicated most time to implement new heroku features to Open Event. Due to the fact that wasn’t easy tasks I can share my experience.

What is heroku?

Heroku is a cloud Platform-as-a-Service (PaaS) supporting several programming languages like Java, Node.js, Scala, Clojure, Python, PHP, and Go

Easy to deploy

what you need to do:

  1. Create account on heroku
  2. Download a heroku toolbelt to your enviroment.
  3. Go to your project directory(/open-event-orga-server)
  4. Sign in to heroku in your command line using your credentials
    $ heroku login
  5. Create app with name $
    heroku apps:create your_app_name

    after execution above command you will recive a link to your application
  6. Push latest changes to heroku
    $ git push heroku master
  7. If everythings is ok you can check results (sometimes it does not work like we want 🙁 )

Easy to configure

To list/set/get config variables use:

$ heroku config
$ heroku config:set YOUR_VARIABLE=token123
$ heroku config:get YOUR_VARIABLE

or you can go to you application dashboard and make above operations

How you can get access to this variables using python langauges?

$ python

Python 2.7.10 (default, Oct 23 2015, 19:19:21) 

[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin

Type "help", "copyright", "credits" or "license" for more information.

>>> import os

>>> your_variable  = os.environ.get('YOUR_VARIABLE', None)

>>> print your_variable

I’ve used this to display current release in my python application(You need to generate a special API token and add it to config variables)

os.popen('curl -n -H 
"Authorization: Bearer ' + token + '" -H 
"Accept: application/vnd.heroku+json; version=3"').read()

Easy to monitor

If something is wrong with your APP you need to use this command

$ heroku logs

it shows all logs

To see 10 latest releases use:

$ heroku releases

How you can set up Open Event to deploy to heroku?

  1. Clone
  2. Go to directory of open event orga server(/open-event-orga-server)
  3. Add git remote
     heroku git:remote -a open-event
  4. You can check if open event is added to git remote
    $ git remote -v
    heroku (fetch)
    heroku (push)
    origin (fetch)
    origin (push)
  5. Now you can deploy changes to open-event application(You need a permissions 🙂 )

Why should you use a Heroku?

It’s great to deploy apps because you are able to share content in short time what I’ve done. Besides it’s very well documented so you can find there answers for most of your questions. Finally most of things you can configure using Heroku dashboard so it’s the best advantages of this tool.

Continue Reading Why should you use Heroku?

Better fields and validation in Flask Restplus

We at Open Event Server project are using flask-restplus for API. Apart from auto-generating of Swagger specification, another great plus point of restplus is how easily we can set input and output models and the same is automatically shown in Swagger UI. We can also auto-validate the input in POST/PUT requests to make sure that we get what we want.

@api.expect(EVENT_POST, validate=True)
def put(self, id):
    """Modify object at id"""

As can be seen above, the validate param for namespace.expect decorator allows us to auto-validate the input payloads. This used to work well until one day I realized there were a few problems.

  1. When a field was defined as say for example field.Integer, then it will accept only Integer values, not even null.
  2. If there is a string field and it has required param set to True, then also it is possible to set empty string as its value and the in-built validator won’t catch it.
  3. Even if I somehow managed to hack my way to support null in field, it will also support null even if required=True.
  4. We had no control on what error message was returned.
EVENT = api.model('Event', {
    'id': fields.Integer,
    'name': fields.String(required=True)

Specially problem #1 was a huge one as it questioned the whole foundation of the API. So we realized it will be better if we don’t use namespace.expect and use a custom validator. For custom validator, we first had to create custom fields that this validator can benefit from. Luckily flask-restplus comes with a great API for creating custom fields. So we quickly created custom fields for all common fields (Integer, String) and more specific fields like Email, Uri and Color. Creating these specific fields were a huge advantage as now we can show proper example for each field types in the Swagger UI.

class Email(fields.String):
    Email field
    __schema_type__ = 'string'
    __schema_format__ = 'email'
    __schema_example__ = '[email protected]'

Consider the above code; now when we use Email as a field for a value, then the example shown for it in Swagger UI will be ‘[email protected]’. Quite cool, right?

Now we needed a way to validate these fields. For that, what we did was to create a validate method in each of the field-classes. This validate method would get the value and check if it was valid. Consider the following code –

import re
EMAIL_REGEX = re.compile(r'[email protected]S+.S+')

class Email():
	def validate(self, value):
		if not value:
		    return False if self.required else True
		if not EMAIL_REGEX.match(value):
		    return False
		return True

Once each of the field had their validate methods, we created a validate_payload() function that uses the API model and compares it with the payload. It will first check if all required keys are present in the payload or not. When that is true, it finally validates each field’s value using their field’s classvalidate method.

from flask import abort
from flask_restplus import fields
from custom_fields import CustomField

def validate_payload(payload, api_model):
    # check if any reqd fields are missing in payload
    for key in api_model:
        if api_model[key].required and key not in payload:
            abort(400, 'Required field '%s' missing' % key)
    # check payload
    for key in payload:
        field = api_model[key]
        if isinstance(field, fields.List):
            field = field.container
            data = payload[key]
            data = [payload[key]]
        if isinstance(field, CustomField) and hasattr(field, 'validate'):
            for i in data:
                if not field.validate(i):
                    abort(400, 'Validation of '%s' field failed' % key)

The CustomField is the base class that each of the custom fields mentioned above inherit. So checking if field was an instance of CustomField is enough to know if it is a custom field or not. Other thing that may look weird in the above code is use of fields.List. If you look closely, I have added this to support custom fields inside lists. So if you have used a custom field in a list, it will also work too. But obviously, this only supports single level lists for now. The thing is we didn’t needed more than that so I let it go. :stuck_out_tongue_winking_eye:

This basically sums up how we are validating input payloads at Open Event. Of course this is very basic but we will keep on improving it as the project progresses. Stay tuned to opev blog if you want to be in touch with the progress of the project.

Links to full code at the time of writing this post are –

  1. Custom Fields
  2. Validate Payload

I hope you found this post useful. Thanks for reading.


{{ Repost from my personal blog }}

Continue Reading Better fields and validation in Flask Restplus

A look into the knitting pattern format

We are currently working on a format that allows to exchange instructions for knitting independent of how it is going to be knit: by a machine like Brother, Pfaff, or by hand.

For this to be possible we need the format to be as general as possible and have no ties to a specific form of knitting. At some point the transition to a format specifically designed for a certain machine is necessary. However, we believe that it is possible to have a format so general that instructions for all types of machines and for hand knitting could be generated from it.

We have decided to use JSON as the language to describe the format, because it is machine readable and human readable at the same time.
The structure of our format follows the rows in knitting.

Our first thought was about creating a format that would describe how meshes are connected and how the thread travels. This would allow for great flexibility and it should be possible to represent everything like this. However, we have decided against it, because we think this format would be quite complicated (different orientations and twists of meshes possible, different threads for multiple colors…) and would get quite big very quickly, because of all the different properties for each mesh and because of all the meshes. Furthermore, and this point is probably more important, knitters do not think this way. If we had a format like that, it would not be easy to understand what was happening for human beings.

Whenever I knit by hand, I never think about how all the meshes are connected by this single thread I am using. I always think about which operations I am performing when knitting in each row. Instructions for creating patterns in knitting are also written this way. They give the knitter a set of knitting instructions to do and possibly repeat. We have concluded, that most knitters think in knitting operations performed rather than connections between meshes.


Knitting instructions from Garnstudio’s Café.

Therefore we have decided to base our format on knitting instructions/operations. The most common instructions probably are: knit, purl, cast on, bind off, knit two together, yarn over. Of course for increases and decreases there are many different operations which work in a similar way but have slight differences (e.g. skp, k2tog).
Since in knitting many things are possible and it is unlikely that we ever manage to create a complete list of all the possible operations you can perform in knitting we have decided to have a very open format, that allows the definition of new instructions.

Instructions are also defined in JSON format. Here is an example, the “k2tog” instruction:

        "type" : "k2tog",
        "title" : {
            "en-en" : "Knit 2 Together"
        "number of consumed meshes" : 2,
        "description" : {
            "wikipedia" : {
                "en-en" : ""
            "text" : {
                "en-en" : "Knit two stitches together, as if they were one stitch."


Knitting patterns consist of multiple rows, which consist of multiple instructions. Furthermore we want to define the connections between rows. This is important, so we can express gaps or slits which are multiple rows long. For example when knitting pants the two legs will be separate. They will be knit separately and their combined width will be increased in comparison to the width of the hip.
Here is an example for a pattern which specifies a cast on in the first row, then a row where all stitches are knit, then the last row is bound off.

  "type" : "knitting pattern",
  "version" : "0.1",
  "comment" : {
    "content" : "cast on and bind off",
    "type" : "markdown"
  "patterns" : [
      "id" : "knit",
      "name" : "cobo",
      "rows" : [
          "id" : 1,
          "instructions" : [
            {"id": "1.0", "type": "co"},
            {"id": "1.1", "type": "co"},
            {"id": "1.2", "type": "co"},
            {"id": "1.3", "type": "co"}
          "id" : 2,
          "instructions" : [
            {"id": "2.0"},
            {"id": "2.1"},
            {"id": "2.2"},
            {"id": "2.3"}
          "id" : 3,
          "instructions" : [
            {"id": "3.0", "type": "bo"},
            {"id": "3.1", "type": "bo"},
            {"id": "3.2", "type": "bo"},
            {"id": "3.3", "type": "bo"}
      "connections" : [
          "from" : {
            "id" : 1
          "to" : {
            "id" : 2
          "from" : {
            "id" : 2
          "to" : {
            "id" : 3

Connections are defined “from” one row “to” another.  The ids identify the rows. The optional attribute start defines the mesh where the connection starts. If start is not defined, the first mesh of the row is assumed. When indexing the list of meshes in a row the first index is 1. The optional attribute “meshes” describes how many meshes will be connected, starting from the mesh defined in “start”.

The resulting parsed Python object structure looks like this:

row model

The Python object structure for working with the parsed knitting pattern.

Each row has a list of instructions. Each instruction produces a number of meshes and consumes a number of meshes. These meshes are also the meshes that are consumed/produced by the rows.


Continue Reading A look into the knitting pattern format

Improvements in sTeam shell and export script

(ˢᵒᶜⁱᵉᵗʸserver) aims to be a platform for developing collaborative applications.
sTeam server project repository: sTeam.


A breakthrough in sTeam development took place. Siddhant Gupta was able to successfully implement the TLS protocol in pike.

The Logs were displayed erroneously. Also the user was not able to scroll down the log to the latest message.  The golden ratio script used in the program was updated to it’s latest version.

Also the editor is opened using sudo command so as to access the vim scrips in the /usr/local/lib/steam/tools directory.
The files are opened using the vim command:
vim -S script -c edit filename1|sp filename2

Only one buffer is accessible at a time. In order to switch the buffer to log buffer the command is CTRL+Ww. Enter this command directly without entering the vim terminal using :.
The log buffer would be accessible, can be edited and scrolled down to the latest log message.

Issue. Github Issue Github PR
Access the log window till the end. Issue-20 PR-48
Open appropriate log window when a sTeam command is executed. Issue-49 PR-51


The logs were displayed erroneously. Ideally the log should be displayed based on the buffer where the sTeam function is called and accordingly the relevant log buffer should be called and display the output. This error was fixed.


The log is displayed in the file named after the file which is opened and concatenated with the suffix “-disp”. In Vi the  :% buffer stores the value of the current file. Consequently this value was concatenated with “-disp” to display the buffer accordingly. The Vi script can be seen below.

Export to git script.

The  export-to-git script was tested vigorously.  All the known issue’s based on the script were replicated in the system and solution was found for them.

The export to git script is now capable of exporting multiple sources at a time. If the last argument is always the target repository then any number of previous arguments can be sources.

Issue. Github Issue Github PR
Support Multiple Source arguments Issue-14 Issue-19 PR-54
Include Source-name in branch name and add branch description. Issue-9 PR-55

Example command :
./export-to-git.pike /home/sTeam/file1 /home/coder/file2 /home/sTeam/container3 ~/gitfolder

The export-to-git script also exports the source name in the branch.  To help distinguish between the branches, we need more descriptive names:

./export-to-git.pike /sources/ /tmp/export-test/

This should create the branch sources-cur_time. Also when a file is specifically exported:

./ecport-to-git.pike /home/coder/demo1.txt /temp/export-test/

would create a branch with name home/coder/demo1.txt-cur_time. This is done to avoid ambiguity between files with same name existing in different locations.
Also a description is added fo the branch name using the git command
git config branch.<branch name>.description "describe branch"
To view this description go to the folder where the branch is exported and then enter the git command
git config branch.<branch name>.description

Export to Git script executing when Multiple Source arguments passed and the modified branch name.

Branch Description


Checkout the FOSSASIA Idea’s page for more information on projects supported by FOSSASIA.

Continue Reading Improvements in sTeam shell and export script

Export Timeline as iCal

iCal or iCalendar is the Internet standard for sharing event schedules or session timelines among each other. The filename extension for iCal is .ics . It is supported by a number of applications such as Google Calendar, Apple Calendar,] Yahoo! Calendar, Lightning extension for Mozilla Thunderbird and SeaMonkey, and partially by Microsoft Outlook . In Open Event Organizer’s Server we have added a feature to export the Schedule of a particular Event in iCal format.

Continue Reading Export Timeline as iCal
Close Menu