Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

This blog is the third installment in the series of blogs Becoming a Ninja Web IDE Developer”.

Link to the first blog can be founder here: WebIDE Ninja #1: Consume 3rd Party services with UI5

Link to the second blog can be found here: WebIDE Ninja #2: Create template which generate Right Pane WebIDE plugin

Fast Forward

For those of you who cannot wait or simply want to use slack in WebIDE.

To use the fast forward, please go through the following steps:

  1. Clone GitHub - ranhsd/WebIDE-Slack into your WebIDE workspace (File > Git > Clone Repository)
  2. expand the folder and select plugin.json
  3. Right click on plugin.json > Run > Run Plugin Project
  4. Click on the slack icon which will be located on right side pane of WebIDE
  5. Watch the video below which explain how to use the plugin.


Prerequisites

Introduction

In the previous blog (#2) we showed how to develop WebIDE template which generate right pane plugin.

Generating/Creating a plugin is only the first step of the plugin development lifecycle, the second step is the plugin implementation (both UI and business logic).

In this blog post we will cover the second step and will show how to implement a "real" plugin.

slack.com - https://slack.com/

What is slack (from their website):


Slack is a messaging app for teams. It brings all your team’s communication and files in one place, where they’re instantly searchable and available wherever you go. Come explore some of our main features and see why thousands of customers love us.


































Since it launch, Slack has become one of the favorite tools for development team around the world. We've piloted this integration a year ago in TechEd Barcelona and got very good responses, so we figured its about time to show you how to do this on your own :smile:

For us Slack brings collaboration capabilities into WebIDE. By integrating slack in WebIDE we can allow developers to chat with each other in real time, get automatic updates via slack bots (will not be covered in this blog post), integrate with other 3rd party solution via slack integrations and more.

In this blog post we will leverage slack real time messaging capabilities and show how you can chat with your team members in real time without leaving WebIDE.

Let's get started!

Create or join a slack team and generate test token

The first thing that we need is a slack team that we are part of. If you are not a member in any team or this is the first time you use slack you can create a new team. In order to do so, go through the following steps:

  • go to https://slack.com
  • on the top right corner select Sign in if you already have account and team in slack, otherwise select create new team

       

  • Next we will need to generate a test token in order to use slack real time messaging API's. To generate a test token, go through the following steps:
    • go to https://api.slack.com/web
    • scroll to the bottom until you see Generate test token green button
    • Click on the Generate test token button to generate a new test token
    • Copy the generated token from the table and save it since we will need it later.

Import an empty right pane plugin into your WebIDE workspace

In order to import the right pane plugin, go through the following steps:

  1. Download the SlackRTM.zip.txt file which attached to this blog post and change the file extension to zip
  2. Import it into your WebIDE workspace (In WebIDE select Workspace folder > Right Click > Import > From File System)
  3. Expand the SlackRTM folder and select plugin.json file
  4. Right click > Run > Run Plugin Project.
  5. A new WebIDE instance will be opened and you will be able to see the slack icon on the right side pane



Implement the chat view

In this section we will implement the view part of our chat. In this view we would like to show the following data:

  • Logged in user name and profile picture
  • ComboBox which allow the user to select a channel (the slack chat system is divided into channels)
  • Settings button that will open a dialog so the user will be able to paste his/her test token (the token that was created above)
  • List of chat history that will be fetched from slack.com api
  • Input field where the user can post a new message to slack

In additional to the fields we will need also to add some css (custom style sheets) that will make our plugin look nice.

At the end our plugin should look like the following:



Pane.view.js code

The Pane.view.js is where we will create all the controls of our view. Our view will be built from various UI5 controls like: BorderLayout, RowRepeater, TextArea,Image and more.

In order to implement the chat view, go through the following steps:

  • Double click on Pane.view.js file which located under the view folder of our plugin

  this.oLayout = new sap.ui.commons.layout.BorderLayout({
  width: "100%",
  height: "100%",
  begin: new sap.ui.commons.layout.BorderLayoutArea({size: "0px"}),
  end: new sap.ui.commons.layout.BorderLayoutArea({size: "0px"}),
  center: this.getCenter(oController),
  top: this.getTop(oController),
  bottom: this.getBottom(oController)
  });
  return this.oLayout;

























          this function create the main layout of our chat, next we will need to create the content for all the different areas for our layout (center,top,bottom)


  • Under the createContent function we will need to create additional functions that will create the UI for different parts of the layout (center,top,bottom). To do it you can copy and paste the following code right below the createContent function.


  getCenter: function(oController) {
  this.oRowRepeater = new sap.ui.commons.RowRepeater("messages_row_repeater",{
  // noData: new sap.ui.commons.TextView({text: "No Messages..."}),
  design: sap.ui.commons.RowRepeaterDesign.BareShell,
  numberOfRows: 0,
  noData: new sap.ui.commons.TextView()
  }).addStyleClass("row-repeater");
  var oCreatedByImage = new sap.ui.commons.Image({
  width: "36px",
  height: "36px",
  src: "{image_72}"
  }).addStyleClass('user-profile-picture');
  var oCreatedByName = new sap.ui.commons.TextView({
  design: sap.ui.commons.TextViewDesign.Bold,
  text: "{createdByName}"
  }).addStyleClass('created-by-name-tv');
  var oMessage = new sap.ui.commons.TextView({
  text: "{message}",
  width: "80%"
  }).addStyleClass('message-content');
  var oCreatedAt = new sap.ui.commons.TextView({
  text: "{createdAt}"
  }).addStyleClass("created-at-tv");
  var oContentLayout = new sap.ui.layout.VerticalLayout({
  content: [oCreatedByName, oCreatedAt,oMessage]
  });
  this.oRowLayout = new sap.ui.layout.HorizontalLayout({
  width: "100%",
  content: [oCreatedByImage,oContentLayout]
  }).addStyleClass('row-layout');
  this.oRowRepeater.bindRows("/data", this.oRowLayout);
  var oLayout = new sap.ui.commons.layout.BorderLayoutArea("messages-row-layout",{
  size: "75%",
  width: "100%",
  contentAlign: "left",
  visible: true,
  content: [this.oRowRepeater]
  }).addStyleClass('bl-center-content');
  return oLayout;
  },
  getBottom: function(oController) {
  this.oPostTextArea = new sap.ui.commons.TextArea({
  rows: 1,
  width: "100%",
  placeholder: "Write something nice...",
  liveChange: [oController.onPostLiveChange,oController]
  }).addStyleClass('new-post-ta');
  var sImagePath = require.toUrl('slack') + "/img/send.png";
  this.oPostButton = new sap.ui.commons.Image({
  src: sImagePath,
  width: "25px",
  height: "25px",
  visible: false,
  press: [oController.onPostButtonPressed,oController]
  });
  var oAbsLayout = new sap.ui.commons.layout.AbsoluteLayout({
  width: "100%"
  }).addStyleClass('post-abs-layout');
  oAbsLayout.addContent(this.oPostTextArea,{left: "0px",top: "0px"});
  oAbsLayout.addContent(this.oPostButton,{right: "20px",top: "12px"});
  var oLayout = new sap.ui.commons.layout.BorderLayoutArea({
  size: "10%",
  contentAlign: "center",
  visible: true,
  overflowY: 'hidden',
  overflowX: 'hidden',
  content: [oAbsLayout]
  }).addStyleClass('bl-bottom-content');
  return oLayout;
  },
  getTop: function(oController) {
  var sImagePath = require.toUrl('slack') + "/image/slack.png";
  var oIcon = new sap.ui.commons.Image({
  width: "32px",
  height: "32px",
  src: "{user_image}"
  }).addStyleClass('slack-icon');
  var oTitleTextView = new sap.ui.commons.TextView({
  text: "Logged in as {user_fname}"
  }).addStyleClass('slack-title-tv');
  this.oChannelsComboBox = new sap.ui.commons.ComboBox({
  width: "90%",
  change: [oController.onChannelChanged,oController]
  }).addStyleClass('slack-channels-cb');
  var oChannelListItem = new sap.ui.core.ListItem({
  "text" : "{name}",
  "key" : "{id}"
  });
  this.oChannelsComboBox.bindItems("/channels",oChannelListItem);
  var oSettingsIcon = new sap.ui.commons.Button({
  text: "Settings",
  lite: true,
  width: "auto",
  press: [oController.onSettingsButtonClicked,oController]
  }).addStyleClass('settings-button');
  this.oSettingsDialog = new sap.ui.commons.Dialog({
  title: "Slack Configurations",
  content: this.getSlackConfigurationsContent(oController),
  modal: true,
  buttons : [
  new sap.ui.commons.Button({
  text : "Close",
  press: [oController.onConfigurationSaveChangesButtonClicked,oController]
  }),
  new sap.ui.commons.Button({
  text : "Save Changes",
  press: [oController.onConfigurationSaveChangesButtonClicked,oController]
  })
  ]
  });
  var oHLayout = new sap.ui.layout.HorizontalLayout({
  width: "100%",
  content: [oIcon,oTitleTextView]
  });
  var oVLayout = new sap.ui.layout.VerticalLayout({
  width: "100%",
  content: [oHLayout,this.oChannelsComboBox]
  }).addStyleClass('top-layout');
  var oLayout = new sap.ui.commons.layout.BorderLayoutArea({
  size: "15%",
  contentAlign: "left",
  visible: true,
  overflowY: 'hidden',
  overflowX: 'hidden',
  content: [oVLayout,oSettingsIcon]
  }).addStyleClass('bl-top-content');
  return oLayout;
  },
  getSlackConfigurationsContent: function(oController) {
  var oAuthTokenLabel = new sap.ui.commons.Label({
  text: "Authentication Token"
  });
  this.oAuthTokenTextField = new sap.ui.commons.TextField({
  placeholder: "paste your slack authentication token here",
  required: true,
  change: [oController.onAuthTokenValueChanged,oController]
  }).addStyleClass('auth-token-text-field');
  var oRemoveTokenButton = new sap.ui.commons.Button({
  text : "Remove",
  lite: true,
  press: [oController.onRemoveTokenButtonClicked,oController]
  }).addStyleClass('remove-token-button');
  // var oTokenHLayout = new sap.ui.layout.HorizontalLayout({
  // width: "100%",
  // content: [oRemoveTokenButton,this.oAuthTokenTextField]
  // }).addStyleClass('token-h-layout');
  var oAuthGenLink = new sap.ui.commons.Link({
  text :"Generate slack authentication token",
  target: "_blank",
  href: "https://api.slack.com/web"
  }).addStyleClass('auth-token-link');
  var oVLayout = new sap.ui.layout.VerticalLayout({
  width: "100%",
  content: [oAuthTokenLabel,this.oAuthTokenTextField,oAuthGenLink]
  });
  return oVLayout;
  }
















  • In order to beautify our UI we will need to add some css rules. To add css rules double click on the style.css which located under the css folder and add the following code:


.sc-main-layout {
    background-color: #3f5161;
}
.row-repeater {
    margin-top: 10px;
    height: 100%;
}
.bl-top-content {
  background-color: #3f5161;
}
.bl-center-content {
  background-color: white;
}
.bl-bottom-content {
  background-color: white;
}
.slack-icon  {
  /*position: absolute;*/
  /*top: 50%;*/
  /*transform: translateY(-50%);*/
  /*margin: 0 10px;*/
}
.slack-title-tv {
  color: white;
  font-size: 12px;
  line-height: 32px;
  margin-left: 15px;
}
.top-layout {
  padding: 10px;
  width: "100%";
}
.slack-channels-cb {
  background-color: transparent;
  border: 1px solid white;
  color: #ab9ba9;
  margin-top: 10px;
}
.slack-channel-listitem {
}
.row-layout {
    margin-bottom: 2px;
    padding: 5px;
    margin-left: 2px;
    margin-right: 10px;
    border-bottom: 1px solid #DDD;
    width: 100%;
}
.message-content {
    height: auto !important;
    margin-right: 5px !important;
    color: #3d3c40;
    font-size: 13px;
    word-wrap: break-word !important;
    display: inline-block;
}
.new-post-ta {
  border: 2px solid #E0E0E0;
  font-size: 13px;
  line-height: 1.0rem;
  overflow-y: hidden !important;
  overflow-x: hidden;
  color: #3d3c40;
  max-height: 45px !important;
  width: 95% !important;
  margin-top: 10px !important;
  padding-right: 40px !important;
  outline: 0 !important;
  -webkit-box-shadow: none;
    -moz-box-shadow: none;
    box-shadow: none;
    background-color: transparent;
}
.post-abs-layout {
  width: 100% !important;
}
.user-profile-picture {
    margin-right: 10px;
    border-radius: 5px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    box-shadow: 0 0 1px rgba(255, 255, 255, .8);
    -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .8);
    -moz-box-shadow: 0 0 1px rgba(255, 255, 255, .8);
    transition: opacity .1s ease-out 0s;
    opacity: .8;
}
.created-by-name-tv {
    margin-top: -3px;
    color: #3d3c40;
    font-size: 14px;
    text-transform: capitalize;
}
.created-at-tv {
  color: #babbbf;
  font-size: 11px;
  margin-top: -5px;
}
.settings-button {
  position: absolute;
  right: 20px;
  top: 10px;
  color: white;
  font-size: 13px;
}
.auth-token-link {
  font-size: 13px;
  margin-top: 5px;
}
.auth-token-text-field {
  width: 250px !important;
}
.token-h-layout {
  width: 100% !important;
}
.remove-token-button {
  margin-left: 10px;
  font-color: red !important;
}
.get-started-button {
  color: #2a80b9;
  font-weight: 600;
  font-size: 14px;
}





















  •     If you will try to run the plugin again you will be able to see the skeleton of the chat.

Pane.controller.js code

This file will contain the code of the chat controller. The chat controller will leverage the following capabilities from slack.com api:

  1. Create event handler for the settings button in order to allow the user to fill in the token that was generated above
  2. Create data model that will hold all the data from slack.com api
  3. Pull all relevant data from slack api and update the global model. The data that we pull is: logged in user details, list of chat channels, chat history and more.
  4. Open WebSocket (JS Native) in front of slack real time messaging API endpoint in order to receive and present the messages in real time
  5. Create event handler for post new message button that will POST a new message to slack RTM api .

The code of the chat controller is:


jQuery.sap.require("jquery.sap.storage");
sap.ui.controller("slack.view.Pane", {
  _oSocket: null,
  _vToken: null,
  _vChannel: null,
  _bConnected: false,
  _bAutoGrowthSet: false,
  _oStorage: null,
  _oUserInfo: null,
  /**
  * Called when a controller is instantiated and its View controls (if available) are already created.
  * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
  * @memberOf view.Pane
  */
  onInit: function() {
  /** get the slack authentication token from local storag
  *  Notice! this is not a best practice to store tokens in the local storage
  *  we are doing it for testing only scenarios
  * */
  this._oStorage = jQuery.sap.storage(jQuery.sap.storage.Type.local);
  this._vToken = this._oStorage.get("slackAuthenticationToken");
  // Create the data model skeleton
  var oModel = new sap.ui.model.json.JSONModel({
  user: {},
  data: [],
  channels: [],
  users: []
  });
  sap.ui.getCore().setModel(oModel); // Set model skeleton to the global model
  /**
  * If the user not enter his/her token yet display no data message
  * */
  if (!this._vToken) {
  this.displayNoData();
  } else {
  var that = this;
  // set the token into the dialog input field
  this.getView().oAuthTokenTextField.setValue(this._vToken);
  // test if the token is valid with slack.com api
  this.testAuthenticationToken(this._vToken, function(authResponse) {
  // if the token is valid, fetch logged in user details from api
  if (authResponse.ok === true) {
  var oUser = {
  user_id: authResponse.user_id,
  team_id: authResponse.team_id,
  username: authResponse.user,
  url: authResponse.url,
  profile: {}
  };
  // execute request to get user info
  that.getUserInfo(authResponse.user_id, function(userInfoResponse) {
  if (userInfoResponse.ok === true) {
  // bind user info to data model
  oModel.oData.user.profile = userInfoResponse.user.profile;
  oModel.oData.user_image = userInfoResponse.user.profile.image_72;
  oModel.oData.user_fname = userInfoResponse.user.real_name;
  oModel.oData.user = oUser;
  that.getView().bindElement("/");
  // connect to slack.com socket API's and fetch chat history
  that.connectAndLoadData();
  }
  });
  } else {
  // if the token is invalid, present no data
  this.displayNoData();
  }
  });
  }
  },
  displayNoData: function() {
  this.getView().oRowRepeater.setNoData(new sap.ui.commons.Button({
  width: "100%",
  lite: true,
  text: "Get started here",
  press: [this.onSettingsButtonClicked, this]
  }).addStyleClass('get-started-button'));
  },
  connectAndLoadData: function() {
  this.getView().oRowRepeater.setBusy(true); // display activity indicator while loading data --> it's better to do it with model binding..
  this.connentToSlack(); // connect to slack WebSocket API
  },
  onAfterRendering: function() {},
  /**
  *
  * */
  connentToSlack: function() {
  var that = this;
  // execute reques to rtm (real time messaging) WebSocket api
  $.get("https://slack.com/api/rtm.start", {
  token: this._vToken
  }, function(data) {
  // create the WebSocket native object
  that._oSocket = new WebSocket(data.url);
  that._oSocket.onopen = function() {
  // implement on connetion opened here...
  };
  // on recieve new message implementation
  that._oSocket.onmessage = function(evt) {
  that.getView().oRowRepeater.setBusy(false);
  var parsedData = JSON.parse(evt.data);
  // message of type "hello" means that the user has been connected to slack WebSocket API
  if (parsedData.type === "hello") {
  if (!that._bConnected) {
  that._bConnected = true;
  // Load channels and chat history of the deafult channel
  that.fetchInitialData();
  }
  }
  if (parsedData.type === "message") {
  if (parsedData.channel !== that._vChannel) {
  return;
  }
  var oModel = sap.ui.getCore().getModel();
  if (parsedData.subtype === "message_deleted") {
  that.removeMessageById(parsedData.deleted_ts, oModel);
  } else if (parsedData.subtype === "bot_message") {
  var bot = that.findBotByName(parsedData.username);
  if (bot) {
  oModel.oData.data.push({
  createdByName: bot.profile.real_name,
  message: parsedData.text,
  image_72: bot.profile.image_72,
  createdAt: moment.unix(parsedData.ts).fromNow(),
  ts: parsedData.ts
  });
  }
  } else {
  var user = that.findUserById(parsedData.user);
  if (user) {
  oModel.oData.data.push({
  createdByName: user.real_name !== undefined && user.real_name.length > 0 ? user.real_name : user.name,
  message: parsedData.text,
  image_72: user.profile.image_72,
  createdAt: moment.unix(parsedData.ts).fromNow(),
  ts: parsedData.ts
  });
  }
  }
  that.getView().oRowRepeater.setNumberOfRows(oModel.oData.data.length);
  that.scrollToBottom("messages-row-layout");
  oModel.refresh(false);
  }
  };
  });
  },
  listUsers: function(callback) {
  $.get("https://slack.com/api/users.list", {
  token: this._vToken
  }, callback);
  },
  removeMessageById: function(vMessageId, oModel) {
  var aMessages = oModel.oData.data;
  for (var i = 0; i < aMessages.length; i++) {
  if (aMessages[i].ts === vMessageId) {
  aMessages.splice(i, 1);
  return true;
  }
  }
  return false;
  },
  findUserById: function(vUserId) {
  if (!vUserId) {
  return;
  }
  var user = null;
  var oModel = sap.ui.getCore().getModel();
  for (var i = 0; i < oModel.oData.users.length; i++) {
  user = oModel.oData.users[i];
  if (user.id === vUserId) {
  break;
  }
  }
  return user;
  },
  findBotByName: function(vBotName) {
  var oModel = sap.ui.getCore().getModel();
  for (var i = 0; i < oModel.oData.users.length; i++) {
  var user = oModel.oData.users[i];
  if (user.name && user.name === vBotName) {
  return user;
  }
  }
  },
  fetchHistory: function(vChannelId, callback) {
  $.get("https://slack.com/api/channels.history", {
  token: this._vToken,
  count: 30,
  channel: vChannelId
  // inclusive: 1
  }, callback);
  },
  fetchAvailabelChannels: function(callback) {
  $.get("https://slack.com/api/channels.list", {
  token: this._vToken,
  exclude_archived: true
  }, callback);
  },
  selectDefaultChannel: function(oModel) {
  for (var i = 0; i < oModel.oData.channels.length; i++) {
  var oChannel = oModel.oData.channels[i];
  if (oChannel.is_general) {
  this.oView.oChannelsComboBox.setSelectedKey(oChannel.id);
  this._vChannel = oChannel.id;
  break;
  }
  }
  },
  addMessageToList: function(oSlackMessage, oModel) {
  var user = this.findUserById(oSlackMessage.user);
  if (oSlackMessage.subtype !== undefined && oSlackMessage.subtype === "bot_message") {
  user = this.findBotByName(oSlackMessage.username);
  if (user) {
  console.log(user.profile.image_72);
  }
  } else {
  user = this.findUserById(oSlackMessage.user);
  }
  if (user) {
  oModel.oData.data.unshift({
  createdByName: user.profile.real_name !== undefined && user.profile.real_name.length > 0 ? user.profile.real_name : user.name,
  message: oSlackMessage.text,
  image_72: user.profile.image_72,
  createdAt: moment.unix(oSlackMessage.ts).fromNow(),
  ts: oSlackMessage.ts
  });
  }
  },
  onChannelChanged: function(oEvent) {
  oEvent.preventDefault();
  var oModel = sap.ui.getCore().getModel();
  oModel.oData.data = [];
  oModel.refresh(false);
  this._vChannel = oEvent.oSource.getSelectedKey();
  this.getView().oRowRepeater.setBusy(true);
  var that = this;
  this.fetchHistory(this._vChannel, function(historyData) {
  for (var i = 0; i < historyData.messages.length; i++) {
  that.addMessageToList(historyData.messages[i], oModel);
  }
  that.getView().oRowRepeater.setBusy(false);
  that.getView().oRowRepeater.setNumberOfRows(oModel.oData.data.length);
  oModel.refresh(false);
  that.scrollToBottom("messages-row-layout");
  });
  },
  onPostLiveChange: function(oEvent) {
  if (!this._bAutoGrowthSet) {
  this._bAutoGrowthSet = true;
  $(".new-post-ta").autogrow({
  onInitialize: false,
  fixMinHeight: true
  });
  }
  if (oEvent.oSource.getLiveValue().length === 0) {
  this.getView().oPostButton.setVisible(false);
  } else {
  this.getView().oPostButton.setVisible(true);
  }
  },
  onPostButtonPressed: function(oEvent) {
  var vMessage = this.getView().oPostTextArea.getValue();
  var that = this;
  $.post("https://slack.com/api/chat.postMessage", {
  token: that._vToken,
  channel: that._vChannel,
  text: vMessage,
  // username: "ranhsd",
  as_user: true
  }, function(response) {});
  this.getView().oPostTextArea.setValue("");
  },
  fetchInitialData: function() {
  var oModel = sap.ui.getCore().getModel();
  var that = this;
  // fetch available chat channels for logged in user
  this.fetchAvailabelChannels(function(data) {
  // store list of channels in global model and select the default channel
  oModel.oData.channels = data.channels;
  that.selectDefaultChannel(oModel);
  // fetch the list of users in this channel
  that.listUsers(function(responseData) {
  oModel.oData.users = responseData.members; // store the list of users in the global model
  // fetch chat history of the selected channel
  that.fetchHistory(that._vChannel, function(historyData) {
  // map slack api message object to data model object
  for (var i = 0; i < historyData.messages.length; i++) {
  that.addMessageToList(historyData.messages[i], oModel);
  }
  that.getView().oRowRepeater.setBusy(false);
  that.getView().oRowRepeater.setNumberOfRows(oModel.oData.data.length);
  that.scrollToBottom("messages-row-layout");
  // bind the results to the UI
  oModel.refresh(false);
  });
  });
  });
  },
  fetchLoggedInUser: function() {},
  onSettingsButtonClicked: function(oEvent) {
  var oSettingsToolPopup = this.getView().oSettingsDialog;
  if (oSettingsToolPopup.isOpen()) {
  oSettingsToolPopup.close();
  } else {
  oSettingsToolPopup.open();
  }
  },
  onConfigurationSaveChangesButtonClicked: function(oEvent) {
  // Parent of the button is the dialog
  // so we can use get parent
  oEvent.oSource.getParent().close();
  },
  onAuthTokenValueChanged: function(oEvent) {
  var sValue = oEvent.oSource.getValue();
  var that = this;
  if (sValue.length > 0 && sValue !== this._vToken) {
  this.testAuthenticationToken(sValue, function(data) {
  if (data.ok === true) {
  that._oStorage.put("slackAuthenticationToken", sValue);
  that._vToken = sValue;
  that.connectAndLoadData();
  } else {
  alert('Invalid Authentication token plese use another one');
  that.getView().oAuthTokenTextField.setValue("");
  }
  });
  // xoxp-3704342075-5154985101-5155004893-9964df
  } else if (sValue.length === 0) {
  if (this._oStorage.get("slackAuthenticationToken")) {
  this._oStorage.remove("slackAuthenticationToken");
  }
  }
  },
  onCloseConfigDialogClicked: function(oEvent) {
  oEvent.oSource.getParent().close();
  },
  testAuthenticationToken: function(vToken, callback) {
  $.get("https://slack.com/api/auth.test", {
  token: vToken
  }, callback);
  },
  getUserInfo: function(vUserId, callback) {
  $.get("https://slack.com/api/users.info", {
  token: this._vToken,
  user: vUserId
  }, callback);
  },
  scrollToBottom: function(sDivId) {
  setTimeout(function() {
  var oDiv = $("#" + sDivId);
  if (oDiv[0] !== undefined) {
  oDiv.scrollTop(oDiv[0].scrollHeight);
  }
  }, 0);
  }
});

















Run and test your plugin

After implementing the view and the controller it's time to run and see that everything is working as expected.

In order to run your plugin, do the following:

  • Select plugin.json file
  • Right click
  • Select Run > Run > Run Plugin Project
  • Click on the slack icon that will be located under the Web IDE right pane area
  • Click on the Settings buttons which located on the top right corner of the view
  • Enter the token that you generated above in the input field and click on the Save button
  • If everything went well the plugin will load and present your chat history from slack
  • in order to post a new message you need do write the message content in the input field which located at the bottom and click on the send button

video which shows how to run and test your plugin:

Best Practices

The code above is in "quick and dirty" style, if you have some time and you want to write it according to JS best practices we suggest you to do the following:

  1. Extract all the slack API calls into a separate JavaScript file
  2. Use Promises and not callbacks (in order to avoid "callback hell")
  3. Modify the view to use sap.m libraries

Contributing

The full source code is available on github: GitHub - ranhsd/WebIDE-Slack

If you like to contribute by adding more features, change to architecture (according to JavaScript best practices) or fix some bugs please create a pull request. For those of you who are not familiar with pull request there is a great video tutorial in here: Git &amp;amp; GitHub: Pull requests (10/11) - YouTube

That's it :smile:


3 Comments