Implementing Pagination for listing Badges

Badgeyay project is divided into two parts i.e front-end with Ember JS and back-end with REST-API programmed in Python.

Badgeyay comes with many features for customising the process of generation of Badges. Badgeyay also keeps record of all the badges generated by user and also gives option to directly download the previously generated badges.
All the badges appear on single page which creates problem when a user has generated lot of badges and all the badges listed on single page.

To resolve this issue and make Badgeyay more user friendly I have implemented pagination in listing badges so that if there are more number of badges, user can see the badges listed in multiple pages in my Pull Request.

To implement this, I have used actions and made the changes accordingly.

Let’s get started and understand it step by step.

Step 1:

We will use semantic icons in handlebars for changing pages.

Step 2:

I will add action for changing page to next and previous page.

// components/user-component/my-badges.js 

import Component from '@ember/component';
import Ember from 'ember';
const { inject } = Ember;
export default Component.extend({
  queryParams : ['page'],
  page        : 1,
  notify      : inject.service('notify'),
  actions     : {
    nextPage() {  // Action for Next Page
      let filter = {};
      if (this.page > 1) {
        filter.page = this.page + 1;
        this.get('store').query('my-badges', filter)
          .then(records => {
            if (records.length > 0) {
              this.set('my-badges', records);
              this.set('page', this.page + 1);
            } else {
              this.notify.error('No More Badges found');
            }
          })
          .catch(err => {
            this.get('notify').error('Please try again!');
          });
      } else {
        this.notify.error('No More Badges Found');
      }
    },
    prevPage() {  // Action for Previous Page
      let filter = {};
      if (this.page - 1 > 0) {
        filter.page = this.page - 1;
        this.get('store').query('my-badges', filter)
          .then(records => {
            this.set('my-badges', records);
            this.set('page', this.page - 1);
          })
          .catch(err => {
            this.get('notify').error('Please try again!');
          });
      } else {
        this.notify.error('No More Badges Found');
      }
    }
  }
});

 

Step 3:

Now, We have to make the query for all badges generated by a user on opening the my-badges route in Frontend.

import Route from '@ember/routing/route';

import Ember from 'ember';

const { set } = Ember;

export default Route.extend({
  beforeModel(transition) {
    this._super(...arguments);
  },

  model(params) {
    let filter = {};
    this.set('params', params);
    filter.state = 'all';
    filter.page = params.page;
    return this.get('store').query('my-badges', filter);
  }
});

 

I have implemented the pagination feature for listing all badges by the user.

 

Step 4:

Now run the server to see the implemented changes by following command.

My Badges Route with Pagination Feature:

Now we are done with implementing the pagination feature for listing all badges by the user.

Resources:

  1. Ember Docs –  Link
  2. Badgeyay Repository – Link
  3. Issue Link – Link
  4. Semantic UI –  LInk

 

Continue ReadingImplementing Pagination for listing Badges

Implementing Custom Tab Viewing on click of an Organizer link in Open Event Android

The Open Event Android app shows a list of organizer social links in the Event Details section. The API fetches the type of social link and the link to the page. For example, if a facebook icon based link is clicked, the default browser will open the facebook page of the Organizer. Our aim was to open the page in the app itself so that the user won’t have to juggle between two different apps. This blog will thus illustrate about how the implementation of a CustomTab is done.

We will move onto the SocialLinkViewHolder file. There we will remove the code for which an explicit intent used to be fired everytime a SocialLink was clicked. We initially add the dependency to the app.gradle file.

implementation “com.android.support:customtabs:27.1.1”

We can clearly observe that custom tabs is supported by the Support library of Android.

We add another field in SocialLinksViewHolder model class. This field will be a Context type field. context will be required in future steps.

class SocialLinksViewHolder(itemView: View, private var context: Context) : RecyclerView.ViewHolder(itemView) {

}

Our next step is to change the overridden function  onCreateViewHolder in the the SocialLinks RecyclerAdapter class. We will now return both the view and the parent context in the method. We return parent context, as it is required in the ViewHolder.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SocialLinksViewHolder {

  val view = LayoutInflater.from(parent.context).inflate(R.layout.item_social_link, parent, false)

  return SocialLinksViewHolder(view, parent.context)

}

We now declare the function showCustomTab() in the bind function of the SocialLinkViewHolder class. Error will be resolved as we still haven’t initialised the function showCustomTab(). This function will be fired only when the link is clicked. That’s why we are firing the function in the onClickListener of itemView

fun bind(socialLink: SocialLink) {

 …

 …
  itemView.setOnClickListener{

      setUpCustomTab(context, socialLink.link)

  }

}

Heading onto initialising the function showCustomTab(). The below code represents the function.

private fun setUpCustomTab(context: Context, url: String) {

  var finalUrl = url

  if (!url.startsWith(“http://”) && !url.startsWith(“https://”)) {

      finalUrl = “http://$url

  }
  CustomTabsIntent.Builder()

          .setToolbarColor(ContextCompat.getColor(context, R.color.colorPrimaryDark))

          .setCloseButtonIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_arrow_back_white_cct_24dp))

          .setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left)

          .setExitAnimations(context, R.anim.slide_in_left, R.anim.slide_out_right)

          .build()

          .launchUrl(context, Uri.parse(finalUrl))

}

Breaking the function into two parts. Firstly, we are verifying the validity of the Url. We are appending http:// to the Url if the Url is not present like that. We skip it, if it is indeed a valid Url

var finalUrl = url

  if (!url.startsWith(“http://”) && !url.startsWith(“https://”)) {

      finalUrl = “http://$url

  }

Then, comes the actual part of the CustomTabsIntent builder. We create a Chrome CustomTab by setting properties regarding toolbar colour, close-button icon, starting and ending animations!

  CustomTabsIntent.Builder()

          .setToolbarColor(ContextCompat.getColor(context, R.color.colorPrimaryDark))

          .setCloseButtonIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_arrow_back_white_cct_24dp))

          .setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left)

          .setExitAnimations(context, R.anim.slide_in_left, R.anim.slide_out_right)

          .build()

          .launchUrl(context, Uri.parse(finalUrl))

setToolbarColor is used to set the toolbar colour of the intent. In this case it was our colourPrimaryDark. setCloseButtonIcon() takes a close icon Bitmap as input. setStartAnimations() takes care of the animation which will occur when the Tab is opened.

build will build the CustomTabIntent and launchUrl will launch the TabIntent with the url being finalUrl.

Thus, we can are clearly able to achieve the feat of showing the web page in the app itself using a CustomTab.

Additional Resources

Tags: GSoC18, FOSSASIA, Open Event, Android, CustomTabsIntent

Continue ReadingImplementing Custom Tab Viewing on click of an Organizer link in Open Event Android

Customizing Ember CLI Notify for Badgeyay

Badgeyay project is divided into two parts i.e front-end with Ember JS and back-end with REST-API programmed in Python.

Badgeyay comes with many features for customising the process of generation of Badges. It gives freedom to user to choose Input Badge data which is to be printed on the individual badges, choosing the badge size, applying custom background to the badges and then optional features of font customization helps to generate cool badges.You have to just click on create badges and the generated badge with download link appear at bottom of form. But a problem arises with the generated badges link that after logout/login or generation of new badges just after creating badges one time, the link of the previously created badges is still there which is a bit confusing, as user might think the previous link to be the new link and press on that in order to download and find the old badges downloaded.

To resolve this issue, I have used the power of Ember notify library and customized it to show the generated badges link and disappear after a specified time in my Pull Request and after that user can always see his previously generated badges in My Badges route.

Let’s get started and understand it.

  • Customizing Notify library and making the changes in the send Badge data function to show the generated badge link just after the completion of badge generation process.

import Controller from '@ember/controller';
import { inject as service } from '@ember/service';   

export default Controller.extend({
....
routing : service('-routing'),
notify : service('notify'), // importing Notify service
.....
sendBadge(badgeData) {
      const _this = this;
      let badgeRecord = _this.get('store').createRecord('badge', badgeData);
      badgeRecord.save()
        .then(record => {
          _this.set('badgeGenerated', true);
          _this.set('genBadge', record);
          var notify = _this.get('notify');
          var link   = record.download_link; // Assigning download link to a variable 
          var message = notify.success(  // Configuring the message of notify service
            { html:   // Adding the html in notify message
            '
Badge generated successfully.
'
+ '<p>Visit this<b>' + '<a href=' + link + '> Link </a></b> to download badge.</p>', closeAfter: 10000 }); // Specifying the time of closing the message }) .catch(err => { _this.get('notify').error('Unable to generate badge'); }); },

 

I have implemented the customized notify function to show the badge generation link for a specific time.

  • Now run the server to see the implemented changes by following command.

$ ember serve

 

  • Ember Notify service showing the generated Badges link:

Now, I am done with the implementation of customised notify function to show the badge generation link for a specific time of 10 seconds.

Resources:

  1. Ember Docs –  Link
  2. Ember Notify Docs – Link
Continue ReadingCustomizing Ember CLI Notify for Badgeyay

Implementing User Input guide and using Semantic-UI Tables

Badgeyay project is divided into two parts i.e front-end with Ember JS and back-end with REST-API programmed in Python.

Badgeyay comes with many features for customising the process of generation of Badges. It gives freedom to user to choose Input Badge data which is to be printed on the individual badges, choosing the badge size, applying custom background to the badges and then optional features of font customization helps to generate cool badges. If a helper is not there for the directions to use these features then these features may be difficult to use for a user.

To resolve this issue and make Badgeyay more user friendly I have implemented a User Guide to help user to go through the User Manual before generating Badges in my Pull Request.

To implement user guide, I have used Semantic UI tables to give examples for CSV format and used other components for Manual format.

Let’s get started and understand it step by step.

Step 1:

Create User Guide component with Ember CLI.

$ ember g  component  user-component/user-guide

 

Step 2:

Now, We will use Semantic UI while editing Handlebars.

class="user-guide">
class="ui center aligned huge header">User-Input Guide
class="ui center aligned small header"> Please check what is the "Correct Format"?
class="ui segments">
class="ui segment">
class="ui raised segment">
class="ui header"> class="file excel icon">
class="content"> CSV Format
</div> </div>
class="ui bulleted list">
class="item">The CSV must be uploaded with 5 columns of data.
class="item">Five comma (',') seperated values should be present in the CSV
class="item">See Example Below
<table class="ui celled structured table"> <thead> <tr> <th rowspan="2">First Name</th> <th rowspan="2">Last Name</th> <th rowspan="2">Position</th> <th rowspan="2">Organisation/Project</th> <th rowspan="2">Social Handle</th> </tr> </thead> <tbody> <tr> <td>Deepjyoti</td> <td>Mondal</td> <td>Mentor</td> <td>FOSSASIA</td> <td>@djmgit</td> </tr> <tr> <td>Yash</td> <td>Ladha</td> <td>Developer</td> <td>Badgeyay</td> <td>@yashladha</td> </tr> <tr> <td>Manish</td> <td>Devgan</td> <td>Developer</td> <td>Badgeyay</td> <td>@gabru-md</td> </tr> <tr> <td>Parth</td> <td>Shandilya</td> <td>Developer</td> <td>Badgeyay</td> <td>@ParthS007</td> </tr> </tbody> </table> </div> </div>
class="ui segment">
class="ui raised segment">
class="ui header"> class="edit icon">
class="content"> Manual Data Format
</div> </div>
class="ui bulleted list">
class="item">Format for Manual data is same as CSV's data
class="item">Five comma (',') seperated values on each line is the correct format
class="item">See Example below
class="ui segment">

Deepjyoti,Mondal,Mentor,FOSSASIA,@djmgit

Yash,Ladha,Developer,FOSSASIA,@yashladha

Manish,Devgan,Developer,FOSSASIA,@gabru-md

Parth,Shandilya,Developer,FOSSASIA,@ParthS007

</div> </div> </div> </div>

 

Step 3:

Link it with Create Badges as a tooltip in the first accordian of create badges route.

<a class="ui icon orange button guide" href="{{href-to 'user-guide'}}" data-tooltip="User Input Guide" data-position="right center"><i class="info icon"></i></a>

 

I have implemented the user guide for the user to go through the User Manual before generating Badges.

Step 4::

Now run the server to see the implemented changes by following command.

$ ember serve

 

User Guide Component

Tooltip present in the create badges form.

Now, we are done implementing a User Guide to help user to go through the User Manual before generating Badges.

Resources:

  1. Ember Docs –  Link
  2. Badgeyay Repository – Link
  3. Issue Link – Link
  4. Semantic UI –  LInk
Continue ReadingImplementing User Input guide and using Semantic-UI Tables

Show number of tickets in Orders Under User Fragment

This blog will illustrate about how we, at Open Event Android, have implemented the way to show the number of tickets of every type of ticket bought by the user in Orders Under User fragment itself!

1. Modify ordersUnderUser function in Order API

The ordersUnderUser function in Order API doesn’t include attendees. So, if we we need to show the number of attendees which is basically the number of tickets, we have to include all the attendee ids.

@GET(“/v1/users/{userId}/orders?filter=[{\”name\”:\”status\”,\”op\”:\”eq\”,\”val\”:\”completed\”}]&include=event,attendees&fields[attendees]=id”)

   fun ordersUnderUser(@Path(“userId”) userId: Long): Single<List<Order>>

1. Modify the orderUnderUser function in viewmodel

Now, we have to modify the OrderViewModel where we will make changes in the orderUnderUser function. We create a LiveData of attendees number which will contain a list of attendee ids received from the order.

val attendeesNumber = MutableLiveData<ArrayList<Int>>()

Then, in the orderUnderUser function, we add a line of code of setting the value of the LiveData.

This basically takes the orders as a list and as List<AttendeeId> is a variable inside the object of Order, we map out the same ArrayList full of attendee ids!

           }.subscribe({

               order = it

               attendeesNumber.value = it.map { it.attendees?.size } as ArrayList<Int>

               val query = buildQuery(it)

               if (idList.size != 0)

                   eventsUnderUser(query)

               else

                   progress.value = false

           }

2. Modify OrdersUnderUserFragment code

We add a observer in the Fragment file. The observer will fire up whenever there is a value received for attendeesNumber. It will fire up the function setAttendeeNumber in ordersRecyclerAdapter.

ordersUnderUserVM.attendeesNumber.observe(this, Observer {

   it?.let {

       ordersRecyclerAdapter.setAttendeeNumber(it)

   }

})

3. Modify OrdersRecyclerAdapter code

We add a variable called attendeesNumber in the file. Another function called setAttendeeNumber is also made to set the Attendee number. We then proceed to bind the number to the view holder.

var attendeesNumber =  ArrayList<Int>()

fun setAttendeeNumber(number: ArrayList<Int>) {

   attendeesNumber = number

}

override fun onBindViewHolder(holder: OrdersViewHolder, position: Int) {

   holder.bind(eventAndOrderIdentifier[position].first, clickListener, eventAndOrderIdentifier[position].second, attendeesNumber[position])

}

4. Modify the ViewHolder of an Order

We create a conditional which thus sets up the message of “See N Tickets” or “See 1 Ticket” based on the ticket number.

if (attendeesNumber == 1) {

   itemView.ticketsNumber.text = “See ${attendeesNumber} Ticket”

} else {

   itemView.ticketsNumber.text = “See ${attendeesNumber} Tickets”

}

Thus, this was how, tickets number viewing was implemented in the app.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, Number of Tickets

Continue ReadingShow number of tickets in Orders Under User Fragment

Implementing Forgot Password Feature in the Open Event Android app

This blog will illustrate about how forgot password feature is implemented in the Open Event Android app. This feature is quite necessary for users as the user might forget his or her password whenever he needs to login. Thus, this feature utilises only the email of the user. When clicked on “I forgot my password” option, the user is sent an email to reset his/her password!

1. Create the RequestToken, Email and RequestToken response models

The first step is to create all required models. We actually send a POST request to the server and the body of it contains the RequestToken model. The response received is contained in the RequestToken  response model. We create an Email model for our use.

RequestToken class:-

data class RequestToken(val data: Email)

Email class:-

data class Email(val email: String)

RequestTokenResponse class:-

data class RequestTokenResponse(val message: String)

2. Show the option only when correct Email Id is entered.

We create a verification method to verify the email. The app will only send a POST request using a correct email. This method is present in the ViewModel.

fun showForgotPassword(email: String): Boolean {

   if (email.isNotEmpty() && Patterns.EMAIL_ADDRESS.matcher(email).matches()) {

       return true

   }

   return false

}

2. Update the Login Fragment file.

We add an onclicklistener to the forgot password option. This will call a function in the ViewModel. The function is sendResetPasswordEmail which utilises the actual email of the user entered.

rootView.forgotPassword.setOnClickListener {

   loginActivityViewModel.sendResetPasswordEmail(email.text.toString())

}

3. Update the ViewModel code

We create the sendResetPasswordEmail method. Below code represents that.

fun sendResetPasswordEmail(email: String) {

   compositeDisposable.add(authService.sendResetPasswordEmail(email)

           .subscribeOn(Schedulers.io())

           .observeOn(AndroidSchedulers.mainThread())

           .doOnSubscribe {

               progress.value = true

           }.doFinally {

               progress.value = false

           }.subscribe({

               requestTokenSuccess.value = verifyMessage(it.message)

           }, {

               error.value = “Email address not present in server. Please check your email”

           }))

}

3. Create an observer for the requestTokenSuccess variable in the Fragment.

Thus, the final task at hand is to create an observer to observe the vaue of requestTokenSuccess variable. Once the variable is set to a message “Email Sent”, we can clearly notify the user that the email is sent perfectly! (The feature image represents this)

Thus, this is how we have implemented the forgot password feature in the app.

References

Tags: GSoC18, FOSSASIA, Open Event, Android, Forgot Password

Continue ReadingImplementing Forgot Password Feature in the Open Event Android app

Adding Acknowledgements section in the Settings of the app

The Open Event Android app uses innumerable open source softwares. Fortunately, android allows us to give credit to each OSS (Open Source Software). We can do this just by writing a few lines of code. In this blog we will illustrate about how we are showing a list of acknowle- dgements in the Open Event Android app.

First of all we will add all the dependencies required. In the app level gradle file, we will add the plugin.

apply plugin: ‘com.google.gms.oss.licenses.plugin’

We then add the implementation of oss licences in the app level file of build.gradle

implementation ‘com.google.android.gms:play-services-oss-licenses:16.0.0’

After that we add the classpath to the project level gradle file in dependencies.

classpath “com.google.gms:oss-licenses:0.9.2”

The google() function must be added in maven for this to work. So, after adding all these dependencies, we click the Sync button in Android Studio. We wait for the project to refresh.

In our Settings Fragment file, we add another preference called “Acknowledgements”. Let us head into the code below. In the SettingsFragment file.

if (preference?.key == resources.getString(R.string.key_acknowledgements)) {

startActivity(Intent(context, OssLicensesMenuActivity::class.java))

return true

}

As we can see, we have added a conditional in the onPreferenceClickListener. If the user clicks on a preference whose key is “key_acknowledgements”, an OssLicensesMenyActivity opens in the app. This is a predefined activity and is only available after we have successfully integrated oss licences. Heading onto some xml code for the feature. We move onto settings.xml file.

<Preference

android:key=”@string/key_acknowledgements”

android:title=”@string/acknowledgements_settings” />

This section is present under the main PreferenceScreen. It is nested and is actually present under a PreferenceCategory named “About”. The final thing we do to complete the process successfully is store our strings in the strings.xml file.

<string name=”key_acknowledgements”>key_acknowledgements</string>

<string name=”acknowledgements_settings”>Acknowledgements</string>

Thus, this is how we are able to show the user a list of acknowledgements successfully in the Open Event android app.

Additional Resources

Tags: GSoC18, FOSSASIA, Open Event, Android, OSS

Continue ReadingAdding Acknowledgements section in the Settings of the app

Add feature to view slides and videos of sessions in Open Event Webapp

The Open Event Web App has two components :

  • An event website generator
  • The actual generated website output.

The web generator application can generate event websites by getting data from event JSON files and binary media files, that are stored in a compressed zip file or through an API endpoint. The JSON data format of version 1 as well as version 2, provides user an option to add the slide and video URLs of the sessions. The data from JSONs is extracted and stored in the objects for a particular session, and in the template, the data for videos and slides are rendered in their corresponding iframes.

Extracting data from event JSONs

The data is extracted from the JSONs and is stored in an object. The object containing the data is sent to the procedure which compiles the handlebars templates with that data.

JSON data format v1

video: session.video,
slides: session.slides,
audio: session.audio

 

JSON data format v2

video: session['video-url'],
slides: session['slides-url'],
Audio: session['audio-url']

 

The JSON data format for v1 and v2 are different and thus the data is extracted from the file depending on API version chosen for web app generation. The files where data extraction takes place are fold_v1.js and fold_v2.js for API v1 and v2 respectively.

Adding event emitter

Onclick event emitter on schedule division calls the procedure “loadVideoAndSlides” with the parameters corresponding to the session clicked.

<div class="schedule-track" id="{{session_id}}" onclick = "loadVideoAndSlides('{{session_id}}', '{{video}}', '{{slides}}')">
   .....
   .....
</div>

The parameters Session ID, Video URL and Slide URL are passed to the procedure which is responsible for displaying the slides and video iframes for the sessions. This resolves the problem of heavy data binding to the page, as the frames for videos and slides are loaded on page only when the session is clicked.

Procedure called on click event

The performance of web app is significantly improved by using the call and listen mechanism as only the requested videos are loaded into the document object model.

function loadVideoAndSlides(div, videoURL, slideURL){
 if(videoURL !== null && videoURL !== '') {
     $('#desc-' + div).children('div').prepend(' + div + '" class = "video-iframe col-xs-12 col-sm-12 col-md-12" src="https://www.youtube.com/embed/' + videoURL + '" frameborder="0" allowfullscreen>');
 }
 if(slideURL !== null && slideURL !== '') {
     $('#desc-' + div).children('div').prepend(' + div + '" class = "iframe col-xs-12 col-sm-12 col-md-12" frameborder="0" src="https://view.officeapps.live.com/op/embed.aspx?src=' + slideURL  +'">');
 }
}

 

The video and slide URLs passed to the procedure are used for loading the iframes from youtube and office apps or google docs respectively as shown above, and the resulting slide view is as shown below

Resources

Continue ReadingAdd feature to view slides and videos of sessions in Open Event Webapp

Adding multiple themes in Open Event Web app

Open Event Web app generator provides an option to event organizers to choose the theme for their event website. Previously it supported the default light theme for the websites, but now it supports a customized dark-theme as well inspired from the twitter dark-mode. Adding this new feature to the User Interface of the generated website, Open Event Web app provides a stylish view for the website of an event.

How we enabled multiple theme support?

The client is provided an option to choose the theme, either ‘light’ or ‘dark’ from the generator. Depending on the option selected, the data is sent to the server, the server extracts the data and while compiling the stylesheet for the pages takes into the account which theme is chosen by the client.

Adding option to choose theme

A drop-down menu is added on the generator page which lets the client choose either of the light or dark themes. The default theme is light.

<label for="theme">Choose your theme</label>
<select class="form-control" id="theme" name="theme">
 <option value="light">Light</option>
 <option value="dark">Dark</option>
</select>

Defining the color schema

The color schema is set different for both the themes, this is achieved by making two different stylesheets for the light and dark themes. The style or color of an element is assigned a variable in main css file(application.scss). Those variables are overwritten while compiling these specific files depending upon the theme chosen.

_light.scss
Defines the values of variables for light theme.

$navbar-color: #ffffff;
$link-color: $gray-dark !default;
$hover-color: $black !default;
$span-background: #efefef !default;

 

_dark.scss
Defines the values of variables for dark theme.

$navbar-color: #2b324a;
$link-color: $light-blue;
$hover-color: $light-blue;
$span-background: #333d5a;

Setting up theme field in json data

The json data to be used for compiling the templates, sets or unsets the theme field according to the theme selected by the client.

if (req.body.theme === 'light') {
 jsonData.theme = 0;
} else {
 jsonData.theme = 1;
}

 

Rendering style in template files

Now we have the value for theme selected by the client, the data used for rendering the CSS file is chosen accordingly. If the theme is light, the file containing color-scheme for light proposition is selected else the dark one. The output file contains the values for color variables according to the theme.

sass.render({
 file: __dirname + '/_scss/_themes/_' + theme + '-theme/_' + theme + '.scss',
 outFile: distHelper.distPath + '/' + appFolder + '/css/schedule.css'
}, function(err, result) {
 if (!err) {
      ...
      ... // Minifying CSS file and writing 
   callback(null);
});

The output file is schedule.css which contains the style for different pages according to the theme chosen while web app generation.

Resources

Continue ReadingAdding multiple themes in Open Event Web app

Implementing API to allow Admins to modify config of devices of any user

As any user can add or remove devices from their account, there needed to be a way by which Admins can manage the user devices. The Admins and higher user roles should have the access to modify the config of devices of any user. This blog post explains how an API has been implemented to facilitate Admins and higher user roles to change config of devices of any user.

Implementing a servlet to allow changing review status of a Skill

The basic task of the servlet is to allow Admin and higher user roles to modify the config of devices of any user. The Admin should be allowed to edit the name of the device and also the room of the device, similar to how a user can edit his own devices.

Here is the implementation of the API:

  1. The API should be usable to only the users who have a user role Admin or higher. Only those with minimum Admin rights should be allowed to control what Skills are displayed on the CMS site. This is implemented as follows:

   @Override
    public UserRole getMinimalUserRole() {
        return UserRole.ADMIN;
    }

 

  1. The endpoint for the API is ‘/cms/modifyUserDevices.json’. This is implemented as follows:

   @Override
    public String getAPIPath() {
        return "/cms/modifyUserDevices.json";
    }

 

  1. The main method of the servlet is the serviceImpl() method. This is where the actual code goes which will be executed each time the API is called. This is implemented as follows:

    JSONObject result = new JSONObject(true);
    Collection<ClientIdentity> authorized = DAO.getAuthorizedClients();
    List<String> keysList = new ArrayList<String>();
    authorized.forEach(client -> keysList.add(client.toString()));
    String[] keysArray = keysList.toArray(new 
    String[keysList.size()]);

    List<JSONObject> userList = new ArrayList<JSONObject>();
    for (Client client : authorized) {
        JSONObject json = client.toJSON();

        if(json.get("name").equals(email)) {
            ClientIdentity identity = new ClientIdentity(ClientIdentity.Type.email, client.getName());
            Authorization authorization = DAO.getAuthorization(identity);

            ClientCredential clientCredential = new ClientCredential(ClientCredential.Type.passwd_login, identity.getName());
            Authentication authentication = DAO.getAuthentication(clientCredential);

            Accounting accounting = DAO.getAccounting(authorization.getIdentity());

            if(accounting.getJSON().has("devices")) {

                JSONObject userDevice = accounting.getJSON().getJSONObject("devices");
                if(userDevice.has(macid)) {
                    JSONObject deviceInfo = userDevice.getJSONObject(macid);
                    deviceInfo.put("name", name);
                    deviceInfo.put("room", room);
                }
                else {
                    throw new APIException(400, "Specified device does not exist.");
                }

            } else {
                json.put("devices", "");
            }
            accounting.commit();
        }
    }

 

Firstly, the list of authorized clients is fetched using DAO.getAuthorizedClients() and is put in an ArrayList. Then we traverse through each element of this ArrayList and check if the device exists by checking if there’s a key-value pair corresponding to the macid passed in the query parameter. If the device doesn’t exist, then an exception is thrown. However, if the macid exists in the traversed element of the ArrayList, then we put the name and the room of the device as passed as query parameters in that particular element of the ArrayList, so as to overwrite the existing name and room of the device of the user.

This is how an API has been implemented which allows Admins and higher user roles to modify the config of devices of any user.

Resources

Continue ReadingImplementing API to allow Admins to modify config of devices of any user