Tuesday, July 30, 2013

Java - Call URL And Sending HTTP Parameters Via GET POST PUT PATCH DELETE Method Easily Execute

Its very easy to invoke URL using Java, We can now send GET, POST, PUT, PATCH And Delete method easily. Invoking URL in Java is easy now. Call URL and get response from that URL is can be done using Java code.

package common;

import java.io.*;
import java.lang.reflect.Field;
import java.net.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;

/**
 * Created by pritom on 11/07/2016.
 */
public abstract class HttpJavaClient {    
    public static enum Method {
        GET, POST, PUT, PATCH, DELETE
    }

    public static enum Type {
        XML, JSON, URL_ENCODED;
    }
    ;

    public static void main(String[] args) throws Exception {
        Response response = doGet("https://pritomkumar.blogspot.com");
        println("Get_Result:\n" + lengthString(response.toString().replace("\n", ""), 400) + "...");

        Map params = new HashMap();
        params.put("param1", "Param1 value");
        params.put("param2", "Param2 value");

        response = doPost("https://pritomkumar.blogspot.com", buildParameters(params), Type.URL_ENCODED);
        println("Post_Result:\n" + lengthString(response.toString().replace("\n", ""), 400) + "...");
    }

    public static Response doGet(String url) {
        return execute(url, Method.GET, "", Type.URL_ENCODED);
    }

    public static Response doGet(String url, Map headers) {
        return execute(url, Method.GET, "", Type.URL_ENCODED, headers, null);
    }

    public static Response doPost(String url, String data, Type type) {
        return execute(url, Method.POST, data, type);
    }

    public static Response doPost(String url, String data, Type type, Map headers) {
        return execute(url, Method.POST, data, type, headers, null);
    }

    public static Response doPut(String url, String data, Type type) {
        return execute(url, Method.PUT, data, type);
    }

    public static Response doPut(String url, String data, Type type, Map headers) {
        return execute(url, Method.PUT, data, type, headers, null);
    }

    public static Response doPatch(String url, String data, Type type) {
        return execute(url, Method.PATCH, data, type);
    }

    public static Response doPatch(String url, String data, Type type, Map headers) {
        return execute(url, Method.PATCH, data, type, headers, null);
    }

    public static Response doDelete(String url) {
        return execute(url, Method.DELETE, "", Type.URL_ENCODED);
    }

    public static Response doDelete(String url, Map headers) {
        return execute(url, Method.DELETE, "", Type.URL_ENCODED, headers, null);
    }

    public static Response execute(String url, Method method, String data, Type type) {
        return execute(url, method, data, type, null, null);
    }

    private static Response execute(String requestURL, Method requestMethod, String requestData, Type dataType, Map headers, Integer timeOutMilli) {
        Long started = System.currentTimeMillis();
        String httpResponse = "", responseMessage = "";
        Integer httpCode = 0;
        timeOutMilli = timeOutMilli == null ? 1000 * 30 : timeOutMilli; /* Default read & write timeout */

        HttpsURLConnection connection = null;
        try {
            String contentType = "", accept = "", contentLength = "" + requestData.length();
            switch (dataType) {
                case XML:
                    contentType = "text/xml; charset=utf-8";
                    accept = "text/xml";
                    break;
                case JSON:
                    contentType = "application/json";
                    break;
                case URL_ENCODED:
                    contentType = "application/x-www-form-urlencoded";
                    break;
            }

            connection = (HttpsURLConnection) new URL(requestURL).openConnection();
            setRequestMethod(connection, requestMethod.name());
            connection.setDoOutput(true);
            connection.setConnectTimeout(timeOutMilli);
            connection.setReadTimeout(timeOutMilli);
            connection.setRequestProperty("Pragma", "no-cache");

            if (headers != null && headers.size() > 0) {
                for (Object name : headers.keySet().toArray()) {
                    if (name.toString().endsWith("tls")) {
                        //headers.put("tls", "TLSv1.2");
                        //headers.put("tls", "TLSv1");
                        SSLContext sc = SSLContext.getInstance(headers.get(name.toString()).toString());
                        sc.init(null, null, new java.security.SecureRandom()); 
                        connection.setSSLSocketFactory(sc.getSocketFactory());
                    }
                    else {
                        connection.setRequestProperty(name.toString(), headers.get(name.toString()).toString());
                    }
                }
            }

            if (requestData.length() > 0) {
                connection.setDoInput(true);

                if (accept.length() > 0) {
                    connection.setRequestProperty("Accept", accept);
                }
                if (contentType.length() > 0) {
                    connection.setRequestProperty("Content-Type", contentType);
                }

                connection.setRequestProperty("Content_length", contentLength);
                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
                writer.write(requestData);
                writer.flush();
                writer.close();
            }

            httpCode = connection.getResponseCode();
            responseMessage = connection.getResponseMessage();
            InputStream is = null;

            if (httpCode >= 200 && httpCode <= 299) {
                is = connection.getInputStream();
            }
            else {
                is = connection.getErrorStream();
            }

            Writer writer5 = new StringWriter();
            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer5.write(buffer, 0, n);
                }
                httpResponse = writer5.toString();
            }
            catch (Exception ex) {
                httpResponse = "";
                responseMessage = ex.getMessage();
            }
        }
        catch (Exception ex) {
            httpResponse = "";
            responseMessage = ex.getMessage();
        }
        finally {
            try {
                connection.disconnect();
            }
            catch (Exception ex10) {
            }
        }

        return new Response(requestURL, httpCode, responseMessage, httpResponse, (System.currentTimeMillis() - started));
    }

    /* JAVA - HttpURLConnection Invalid HTTP method such as 'PATCH' */
    private static void setRequestMethod(final HttpsURLConnection httpURLConnection, final String method) throws Exception {
        try {
            httpURLConnection.setRequestMethod(method);
        }
        catch (final ProtocolException pe) {
            Class<?> connectionClass = httpURLConnection.getClass();
            Field delegateField = null;
            try {
                delegateField = connectionClass.getDeclaredField("delegate");
                delegateField.setAccessible(true);
                HttpsURLConnection delegateConnection = (HttpsURLConnection) delegateField.get(httpURLConnection);
                setRequestMethod(delegateConnection, method);
            }
            catch (NoSuchFieldException e) {
                // Ignore for now, keep going
            }
            catch (IllegalArgumentException e) {
                throw new RuntimeException(e);
            }
            catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            try {
                Field methodField;
                while (connectionClass != null) {
                    try {
                        methodField = connectionClass.getDeclaredField("method");
                    }
                    catch (NoSuchFieldException e) {
                        connectionClass = connectionClass.getSuperclass();
                        continue;
                    }
                    methodField.setAccessible(true);
                    methodField.set(httpURLConnection, method);
                    break;
                }
            }
            catch (final Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static String buildParameters(Map parametersMap) {
        Iterator nameIterator = parametersMap.keySet().iterator();
        StringBuffer buf = new StringBuffer();

        while (nameIterator.hasNext()) {
            Object key = nameIterator.next();
            Object value = parametersMap.get(key);
            if (value == null) {
                value = "";
            }

            if (!(key instanceof String)) {
                throw new IllegalArgumentException("Expected a string key in parametersMap but found " + key);
            }

            if (!(value instanceof String)) {
                throw new IllegalArgumentException("Expected a string value in parametersMap but found " + value);
            }

            String parameterName = (String) key;
            String parameterValue = (String) value;
            if (buf.length() > 0) {
                buf.append('&');
            }

            try {
                buf.append(parameterName);
                buf.append('=');
                buf.append(URLEncoder.encode(parameterValue, "UTF-8"));
            }
            catch (Exception var9) {
                var9.printStackTrace();
            }
        }

        return buf.toString();
    }

    public static void parseParameters(String s, String enc, Map parameters) {
        if (s != null) {
            int start = 0;
            int end = s.length();
            boolean amp = true;

            do {
                int amp1 = s.indexOf(38, start);
                if (amp1 == -1) {
                    amp1 = end;
                }

                int eq = s.indexOf(61, start);
                if (eq == -1 || eq > amp1) {
                    eq = amp1;
                }

                String name = s.substring(start, eq);
                String value = eq == amp1 ? "" : s.substring(eq + 1, amp1);

                try {
                    parameters.put(URLDecoder.decode(name, enc), URLDecoder.decode(value, enc));
                }
                catch (Exception var11) {
                    var11.printStackTrace();
                }

                start = amp1 + 1;
            } while (start < end);
        }
    }

    public static class Response {
        private String url;
        private Integer code;
        private String message;
        private String body;
        private Long time;

        public Response(String url, Integer code, String message, String body, Long time) {
            this.url = url;
            this.code = code;
            this.message = message;
            this.body = body;
            this.time = time;
        }

        @Override
        public String toString() {
            return "{URL=" + this.url + ",Code=" + this.code + ", Message=" + this.message + ", Time=" + this.time + " Millis, Body=" + this.body + "}";
        }

        public Integer getCode() {
            return this.code;
        }

        public String getMessage() {
            return this.message;
        }

        public String getBody() {
            return this.body;
        }

        public Long getTime() {
            return this.time;
        }
    }

    public static void println(Object o) {
        System.out.println("" + o);
    }

    public static String lengthString(String s, Integer m) {
        return s.length() > m ? s.substring(0, m) : s;
    }
}

How to output MySQL query results in csv format

SELECT order_id,product_name,qty
FROM orders
INTO OUTFILE '/tmp/orders.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'

Sunday, July 28, 2013

jQuery check if element has a specific style property defined inline

Here's a very simple (probably in much need of improvement) plugin I've thrown together that will get you the value of an inline style property (and return undefined if that property is not found):

​(function ($) {
    $.fn.inlineStyle = function (prop) {
         var styles = this.attr("style"),
             value;
         styles && styles.split(";").forEach(function (e) {
             var style = e.split(":");
             if ($.trim(style[0]) === prop) {
                 value = style[1];          
             }                   
         });  
         return value;
    };
}(jQuery));

You can call it like this:

//Returns value of "width" property or `undefined`
var width = $("#someElem").inlineStyle("width");



Or you can use more simple way to do this as:

if ( $("#someElem")[0].style["width"] ) {
    //Has the style property inline
}
else {
    //Has not the style property inline
}

How do I call/use Grails service from a gsp

We can use 'grailsApplication.classLoader.loadClass' to load service.

<%@ page import="com.myproject.MyService" %>
<%
def myService = grailsApplication.classLoader.loadClass('com.myproject.MyService').newInstance()
%>
<span>${myService.methodName()}</span>


We can use another approach

In Grails we can use the set tag to define a variable with a value on a GSP. But we can also use the same tag to access a Grails service on a GSP. We must use the bean attribute with the name of the Grails service. We can even pass the name of any Spring managed bean in the application context of our application.
In the following sample GSP we access a Grails service with the name BlogService. We invoke the method allTitles() and use the result.

<html>
<head>
<meta content="main" name="layout"/>
</head>
<body>
    %{--Use BlogService--}%
    <g:set var="blog" bean="blogService"/>
    <ul>
    <g:each in="${blog.allTitles()}" var="title">
        <li>${title}</li>
    </g:each>
    </ul>
</body>
</html>

Creating a CKEditor Plugin with a Custom Dialog Window

Plugin Files

Firstly, we will need to create the simpleLink folder inside the plugins directory of the CKEditor installation.
Remember that for CKEditor the name of the plugin folder is important and has to be the same as the name of the plugin, otherwise the editor will not be able to recognize it.

Inside the newly created simpleLink folder we are going to place the plugin.js file that will contain the plugin logic. Apart from that, since we will also need a toolbar icon for our plugin, we are going to add animages folder and subsequently place the icon.png file inside.
To sum up, we will need the following file structure for our plugin to work:
  • ckeditor root
    • plugins
      • simpleLink
        • images
          • icon.png
        • plugin.js

Plugin Source Code

With the following structure ready, it is time to open the plugin.js file in a text editor and to start creating the source code of the plugin.
CKEDITOR.plugins.add( 'simpleLink',
{
 init: function( editor )
 {
  // Plugin logic goes here...
 }
});
All CKEditor plugins are created by using the CKEDITOR.plugins.add function. This function should contain the plugin name ('simpleLink') and the plugin logic placed inside the init function that is called upon the initialization of the editor instance.

Creating an Editor Command

We want our plugin to have a dialog window, so we need to define an editor command that opens a new dialog window. To do this, we will need to use the addCommand function opening the simpleLinkDialog window that we are going to define in a moment by using the CKEDITOR.dialogCommandfunction.
editor.addCommand( 'simpleLinkDialog', new CKEDITOR.dialogCommand( 'simpleLinkDialog' ) );

Creating a Toolbar Button

The plugin dialog window is to be opened by using a toolbar button. To this end, we need to define a button that will be associated with the dialog window. The CKEditor.ui.addButton function accepts a button name ('SimpleLink') along with the definition of the tooltip text (label) and the button icon (icon). Note that this.path is the directory where the plugin.js file resides.
These parameters are responsible for the button presentation. To make the button actually work, we need to connect it to the plugin command name defined above by using the command parameter.
editor.ui.addButton( 'SimpleLink',
{
 label: 'Insert a Link',
 command: 'simpleLinkDialog',
 icon: this.path + 'images/icon.png'
});

CKEditor Initialization

It is now time to initialize a CKEditor instance that will use the Simple Link plugin along with its toolbar button.
To register the plugin with CKEditor, we have to add it to the extraPlugins list. We also need to enhance the toolbar definition and add the plugin button by using the toolbar parameter.
Open the page that will contain CKEditor in a text editor, and insert a CKEditor instance using the following toolbar and plugin configuration.
<script type="text/javascript">
 
 // Replace the <textarea id="editor1"> with a CKEditor instance using default configuration.
 CKEDITOR.replace( 'editor1',
  {
   extraPlugins : 'simpleLink',
   toolbar :
   [
    ['Source', '-', 'Bold', 'Italic', '-', 'NumberedList', 'BulletedList', '-', 'Link', 'Unlink'],
    ['About','-','SimpleLink']
   ]
  });
 
</script>
After you load the page containing the above CKEditor instance you should be able to see the new plugin toolbar button along with its tooltip.

Plugin Dialog Window

Up till now, most of the actions that we performed were equivalent to what we did while creating the Abbreviation plugin. Here is, however, where the really interesting part begins. We will now move on to creating a custom dialog window along with its contents.
Clicking the button should open the simpleLinkDialog dialog window. First, however, we need to return to the Simple Link plugin source file and define the dialog window by using the CKEDITOR.dialog.add function. To see all dialog window definition elements, refer to CKEditor JavaScript API.
In our case we will give the dialog window a name ('simpleLinkDialog') and use the title, minWidth, and minHeight parameters to define its title and minimum dimensions, respectively.

The name selected for the dialog window is the name that appears in the addCommand function above.

Dialog Window Tab

The dialog window should also contain some contents, so we will begin with adding a tab along with its label. In all CKEditor dialog windows the contents of a dialog window are defined iside the contents array. This array contains CKEDITOR.dialog.contentDefinition objects that consititute the tabs (or "content pages") of a dialog window.
We will give our dialog window tab an id and label. Please note that since in our case the dialog window contains just one tab (or "page"), the tab's name (label) will not be visible. However, even though the label property is not required, it is a good practice to include it since it plays its role in accessibility support as it will be read aloud by screen readers.
Note that by default CKEditor also adds the standard OK and Cancel buttons. If you want to customize them (i.e. remove both or just one, add custom ones), you can use the buttons array to add new button elements.
A dialog window tab created in this way should also contain some real content, like text fields, checkboxes, or drop-down lists. These will be added in the next step inside the elements property.
In order to create the Simple Link plugin dialog window, add the following code in the plugin.js file below the plugin toolbar button definition.
CKEDITOR.dialog.add( 'simpleLinkDialog', function( editor )
{
 return {
  title : 'Link Properties',
  minWidth : 400,
  minHeight : 200,
  contents :
  [
   {
    id : 'general',
    label : 'Settings',
    elements :
    [
      // UI elements of the Settings tab.
    ]
   }
  ]
 };
});
The result of this change can be seen immediately. Click the Insert a Link toolbar button in order to open the newly created (and so far empty) Link Properties dialog window.


Dialog Window Tab Elements

User interface elements that can be added to a dialog window tab are defined in the elements parameter, which is an array ofCKEDITOR.dialog.uiElementDefinition objects.
Since we want to try a number of different types of dialog window UI elements, in this case the dialog window tab will consist of a larger textarea, smaller text field, a selection field (drop-down list), and a checkbox. We will also use HTML to create the tab description.
Each UI element is added inside the elements array, with the definition placed inside the curly braces ({}), separated from one another with a comma. The type parameter is a required one and defines the type of the element.
UI Elements: HTML
The first UI element we are going to use is the HTML type. The html type allows you to define the contents of a dialog window page by using pure HTML code. The HTML code to be placed inside the page is entered in the html parameter.
elements :
[
 {
  type : 'html',
  html : 'This dialog window lets you create simple links for your website.'  
 }
]
After adding the above code to the plugin source, the Link Properties dialog window looks like this.

UI Elements: Textarea
The element to be placed underneath the description is the textarea element that will be obligatory to fill in. We will use it to add the text that is displayed in the document and points to the inserted link.
In order to create a textarea we will use the textarea UI element type and assign it an id. The textarea will also need a label that will describe its purpose and can be added using the label parameter.
Since filling in the contents of the textarea will be obligatory, we will set the required parameter to true and inside the validate parameter add some simple validation that checks whether the field is empty. If it is, the validator will return an error message.
{
 type : 'textarea',
 id : 'contents',
 label : 'Displayed Text',
 validate : CKEDITOR.dialog.validate.notEmpty( 'The Displayed Text field cannot be empty.' ),
 required : true
}
This is the appearance of the Link Properties dialog window after we apply the changes.

The size of the textarea can obviously be customized. If you want to change the element's dimensions, use the cols amd rows parameters as defined in the textarea constructor.
UI Elements: Text Field
Another UI element that we are going to use in the plugin is the text field (text input) element. We will use it to enter the Internet address of the linked page.
A text field lets you enter text into a single-line field and is meant for shorter entries. Its definition is very similar to the textarea — the main difference lies in the type parameter that is set to text.
The id and label parameters will be defined as done above. In our case this field is obligatory, so we will set its required parameter to true. The validation code inside the validate parameter will also be used to check whether the field was filled in. If it is empty, the validator will return an error message.
{
 type : 'text',
 id : 'url',
 label : 'URL',
 validate : CKEDITOR.dialog.validate.notEmpty( 'The link must have a URL.' ),
 required : true
}
With the latest addition of the text field the Link Properties dialog window will look like this.

UI Elements: Selection Field
Another type of UI elements that we are going to try out is the selection field (drop-down list). In this case we will use it to allow the user to choose one of the pre-defined styles for the link text.
To create a selection field we need to set the type parameter of the element to select. As before, we will add the id and label parameters appropriate for this field. The items to be chosen from the selection list are defined inside the obligatory items array that contains the values along with their displayed text. The first item defined in the list will be used by default; you can also use the default parameter to change this setting.
{
 type : 'select',
 id : 'style',
 label : 'Style',
 items : 
 [
  [ '<none>', '' ],
  [ 'Bold', 'b' ],
  [ 'Underline', 'u' ],
  [ 'Italics', 'i' ]
 ]
}
After the selection field is added to the plugin dialog window and is expanded by the user, the style that was selected by default (the first one, if not defined otherwise) is highlighted.

UI Elements: Checkbox
The final UI element to be added to the plugin dialog window is the checkbox. In this case the user will be able to select it if the link is to be opened in a new window.
In order to create a checkbox we need to set the type parameter of the element to checkbox. As before, the field will also get a standard id andlabel. Since we want opening in a new page to be a default behavior, we will set the default parameter to true in order to have the checkbox selected when the plugin dialog window is opened.

 Please note that the default parameter needs to be placed in single quotes since it is a reserved JavaScript word.

 
 
{
 type : 'checkbox',
 id : 'newPage',
 label : 'Opens in a new page',
 'default' : true
}
The figure below shows the Link Properties dialog window complete with all the UI elements added above.

Plugin Behavior

The plugin now looks good — the toolbar button is in place and the dialog window is complete with a couple of sample UI elements. There is, however, one problem: it does not really do anything.
We will start with creating the onOk method that is invoked once the user accepts the changes introduced in the dialog window by clicking the OKbutton or pressing the Enter key on the keyboard. The method will be defined inside the CKEDITOR.dialog.add function, below the definition of dialog window contents.
onOk : function()
{
 // The code that will be executed when the user accepts the changes.
}
We shall start the onOk function from creating a link element and a data object that will store the data entered in the dialog window fields. The link will be based on a standard HTML <a> element and as a new DOM element it will be created by using the createElement function.
var dialog = this,
 data = {},
 link = editor.document.createElement( 'a' );
In order to populate the data object with the contents of the dialog window fields we will use the commitContent function.
this.commitContent( data );
To make the commitContent function work we will however first need to define the commit functions themselves. In order to do that, we will have to revise the code of the dialog window UI elements again.
The commit functions will have to be added to all user input elements of the plugin dialog window — the textarea, the text field, the selection field, and the checkbox. In each case the functions need to get the value entered by the user by using the getValue function and assign it to an appropriate attribute of the data object.
elements :
[
 {
  type : 'html',
  html : 'This dialog window lets you create simple links for your website.'  
 },
 {
  type : 'textarea',
  id : 'contents',
  label : 'Displayed Text',
  validate : CKEDITOR.dialog.validate.notEmpty( 'The Displayed Text field cannot be empty.' ),
  required : true,
  commit : function( data )
  {
   data.contents = this.getValue();
  }
 },
 {
  type : 'text',
  id : 'url',
  label : 'URL',
  validate : CKEDITOR.dialog.validate.notEmpty( 'The link must have a URL.' ),
  required : true,
  commit : function( data )
  {
   data.url = this.getValue();
  }
 },
 {
  type : 'select',
  id : 'style',
  label : 'Style',
  items : 
  [
   [ '<none>', '' ],
   [ 'Bold', 'b' ],
   [ 'Underline', 'u' ],
   [ 'Italics', 'i' ]
  ],
  commit : function( data )
  {
   data.style = this.getValue();
  }
 },
 {
  type : 'checkbox',
  id : 'newPage',
  label : 'Opens in a new page',
  'default' : true,
  commit : function( data )
  {
   data.newPage = this.getValue();
  }
 }
]
We now have the values entered in the plugin dialog window in the data object. It is time to return to the onOk function and start building the contents of the link variable that contains the <a> element.
The URL of the linked page will be added to the <a> element by using the setAttribute function to update the href attribute of the link object with the contents of the url attribute of the data object.
link.setAttribute( 'href', data.url );
If the checkbox setting the link to open in a new page (newPage) was selected, we also need to set the target attribute of the link to _blank by using the setAttribute function again.
if ( data.newPage )
 link.setAttribute( 'target', '_blank' );
If the user decided to apply one of the styles from the drop-down list to the link, we have to add this setting to the link element. We will use a JavaScriptswitch statement to define the style setting based on the user's choice and use the setStyle function to add an appropriate CSS stylesheet rule to the inline style attribute of the link element.
switch( data.style )
{
 case 'b' :
  link.setStyle( 'font-weight', 'bold' );
 break;
 case 'u' :
  link.setStyle( 'text-decoration', 'underline' );
 break;
 case 'i' :
  link.setStyle( 'font-style', 'italic' );
 break;
}
We now need to insert the Displayed Text data (contents) into the link, in between the <a> and </a> tags. In order to achieve this we will use thesetHtml function.
link.setHtml( data.contents );
Finally, the link object has to be inserted into the document, at the position of the cursor. We will use the insertElement function for that.
editor.insertElement( link );

Full Source Code

To see the full contents of the plugin.js file, .

You can also download the whole plugin folder inluding the icon and the fully commented source code.

Working Example

The plugin code is now ready. When you click the Insert Link toolbar button, the custom Link Properties dialog window will open. Fill in the obligatoryDisplayed Text and URL fields, and click the OK button. The checkbox configuring the link to open in a new window is checked by default, but you can uncheck it. You can also change the link style by choosing one of the options from the drop-down list.
The newly added link will be inserted into the document and displayed with a style selected in the dialog window. 

If you switched to Source view, you could see all the information from the plugin dialog window added as link contents and attributes.