    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.plugin;
    020
    021import java.util.Locale;
    022import org.apache.wiki.api.core.Attachment;
    023import org.apache.wiki.api.core.Context;
    024import org.apache.wiki.api.core.ContextEnum;
    025import org.apache.wiki.api.core.Engine;
    026import org.apache.wiki.api.exceptions.PluginException;
    027import org.apache.wiki.api.exceptions.ProviderException;
    028import org.apache.wiki.api.plugin.Plugin;
    029import org.apache.wiki.attachment.AttachmentManager;
    030import org.apache.wiki.parser.MarkupParser;
    031import org.apache.wiki.util.TextUtil;
    032
    033import java.util.Map;
    034import java.util.ResourceBundle;
    035
    036
    037/**
    038 *  Provides an image plugin for better control than is possible with a simple image inclusion.
    039 *  <br> Most parameters are equivalents of the html image attributes.
    040 *
    041 *  <p>Parameters : </p>
    042 *  <ul>
    043 *  <li><b>src</b> - the source (a URL) of the image (required parameter)</li>
    044 *  <li><b>align</b> - the alignment of the image</li>
    045 *  <li><b>height</b> - the height of the image</li>
    046 *  <li><b>width</b> - the width of the image</li>
    047 *  <li><b>alt</b> - alternate text</li>
    048 *  <li><b>caption</b> - the caption for the image</li>
    049 *  <li><b>link</b> - the hyperlink for the image</li>
    050 *  <li><b>target</b> - the target (frame) to be used for opening the image</li>
    051 *  <li><b>style</b> - the style attribute of the image</li>
    052 *  <li><b>class</b> - the associated class for the image</li>
    053 *  <li><b>border</b> - the border for the image</li>
    054 *  <li><b>title</b> - the title for the image, can be presented as a tooltip to the user</li>
    055 *  </ul>
    056 *
    057 *  @since 2.1.4.
    058 */
    059// FIXME: It is not yet possible to do wiki internal links.  In order to do this cleanly, a TranslatorReader revamp is needed.
    060public class Image implements Plugin {
    061
    062    /** The parameter name for setting the src.  Value is <tt>{@value}</tt>. */
    063    public static final String PARAM_SRC      = "src";
    064    /** The parameter name for setting the align parameter.  Value is <tt>{@value}</tt>. */
    065    public static final String PARAM_ALIGN    = "align";
    066    /** The parameter name for setting the height.  Value is <tt>{@value}</tt>. */
    067    public static final String PARAM_HEIGHT   = "height";
    068    /** The parameter name for setting the width.  Value is <tt>{@value}</tt>. */
    069    public static final String PARAM_WIDTH    = "width";
    070    /** The parameter name for setting the alt.  Value is <tt>{@value}</tt>. */
    071    public static final String PARAM_ALT      = "alt";
    072    /** The parameter name for setting the caption.  Value is <tt>{@value}</tt>. */
    073    public static final String PARAM_CAPTION  = "caption";
    074    /** The parameter name for setting the link.  Value is <tt>{@value}</tt>. */
    075    public static final String PARAM_LINK     = "link";
    076    /** The parameter name for setting the target.  Value is <tt>{@value}</tt>. */
    077    public static final String PARAM_TARGET   = "target";
    078    /** The parameter name for setting the style.  Value is <tt>{@value}</tt>. */
    079    public static final String PARAM_STYLE    = "style";
    080    /** The parameter name for setting the class.  Value is <tt>{@value}</tt>. */
    081    public static final String PARAM_CLASS    = "class";
    082    /** The parameter name for setting the border.  Value is <tt>{@value}</tt>. */
    083    public static final String PARAM_BORDER   = "border";
    084    /** The parameter name for setting the title.  Value is <tt>{@value}</tt>. */
    085    public static final String PARAM_TITLE    = "title";
    086
    087    /**
    088     *  This method is used to clean away things like quotation marks which
    089     *  a malicious user could use to stop processing and insert javascript.
    090     */
    091    private static String getCleanParameter( final Map< String, String > params, final String paramId ) {
    092        return TextUtil.replaceEntities( params.get( paramId ) );
    093    }
    094
    095    @Override
    096    public String getDisplayName(Locale locale) {
    097        final ResourceBundle rb = ResourceBundle.getBundle(PluginManager.PLUGIN_I18N_RESOURCE, locale);
    098        return rb.getString(this.getClass().getSimpleName());
    099    }
    100
    101    @Override
    102    public String  getSnipExample() {
    103        return  "Image src='{image.jpg}'";
    104    }
    105
    106    private boolean needsSanitization(String link) {
    107        String testVal = link.toLowerCase().replaceAll("\\s+", "").trim();
    108        if (testVal.startsWith("data")
    109                || testVal.startsWith("javascript")
    110                || testVal.startsWith("vbscript")) {
    111            return true;
    112        }
    113        return false;
    114    }
    115
    116    /**
    117     *  {@inheritDoc}
    118     */
    119    @Override
    120    public String execute( final Context context, final Map<String, String> params ) throws PluginException {
    121        final Engine engine  = context.getEngine();
    122        String src           = getCleanParameter( params, PARAM_SRC );
    123        final String align   = getCleanParameter( params, PARAM_ALIGN );
    124        final String ht      = getCleanParameter( params, PARAM_HEIGHT );
    125        final String wt      = getCleanParameter( params, PARAM_WIDTH );
    126        final String alt     = getCleanParameter( params, PARAM_ALT );
    127        final String caption = getCleanParameter( params, PARAM_CAPTION );
    128        String link          = getCleanParameter( params, PARAM_LINK );
    129        String target        = getCleanParameter( params, PARAM_TARGET );
    130        final String style   = getCleanParameter( params, PARAM_STYLE );
    131        final String cssclass= getCleanParameter( params, PARAM_CLASS );
    132        final String border  = getCleanParameter( params, PARAM_BORDER );
    133        final String title   = getCleanParameter( params, PARAM_TITLE );
    134
    135        if( src == null ) {
    136            throw new PluginException("Parameter 'src' is required for Image plugin");
    137        }
    138
    139        //if( cssclass == null ) cssclass = "imageplugin";
    140
    141        if( target != null && !validTargetValue(target) ) {
    142            target = null; // not a valid value so ignore
    143        }
    144
    145        try {
    146            final AttachmentManager mgr = engine.getManager( AttachmentManager.class );
    147            final Attachment att = mgr.getAttachmentInfo( context, src );
    148
    149            if( att != null ) {
    150                src = context.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), att.getName() );
    151            }
    152        } catch( final ProviderException e ) {
    153            throw new PluginException( "Attachment info failed: " + e.getMessage() );
    154        }
    155
    156        final StringBuilder result = new StringBuilder();
    157
    158        result.append( "<table border=\"0\" class=\"imageplugin\"" );
    159
    160        if( title != null ) {
    161            result.append( " title=\"" ).append( title ).append( "\"" );
    162        }
    163
    164        if( align != null ) {
    165            if( align.equals( "center" ) ) {
    166                result.append( " style=\"margin-left: auto; margin-right: auto; text-align:center; vertical-align:middle;\"" );
    167            } else {
    168                result.append( " style=\"float:" ).append( align ).append( ";\"" );
    169            }
    170        }
    171
    172        result.append( ">\n" );
    173
    174        if( caption != null ) {
    175            result.append( "<caption>" ).append( caption ).append( "</caption>\n" );
    176        }
    177
    178        // move css class and style to the container of the image, so it doesn't affect the caption
    179        result.append( "<tr><td" );
    180
    181        if( cssclass != null ) {
    182            result.append( " class=\"" ).append( cssclass ).append( "\"" );
    183        }
    184
    185        if( style != null ) {
    186            result.append( " style=\"" ).append( style );
    187
    188            // Make sure that we add a ";" to the end of the style string
    189            if( result.charAt( result.length()-1 ) != ';' ) {
    190                result.append( ";" );
    191            }
    192
    193            result.append("\"");
    194        }
    195
    196        result.append( ">" );
    197
    198        if( link != null ) {
    199            if( !context.getBooleanWikiProperty( MarkupParser.PROP_ALLOWHTML, false ) ) {
    200                if (needsSanitization(link)) {
    201                    link = "http://invalid_url" + link;
    202                }
    203            }
    204            result.append( "<a href=\"" ).append( link ).append( "\"" );
    205            if( target != null ) {
    206                result.append( " target=\"" ).append( target ).append( "\"" );
    207            }
    208            result.append(">");
    209        }
    210
    211        if(!context.getBooleanWikiProperty(MarkupParser.PROP_ALLOWHTML, false)) {
    212            if (needsSanitization(src)) {
    213                src = "http://invalid_url" + src;
    214            }
    215        }
    216        result.append( "<img src=\"" ).append( src ).append( "\"" );
    217
    218        if( ht != null ) {
    219            result.append( " height=\"" ).append( ht ).append( "\"" );
    220        }
    221        if( wt != null ) {
    222            result.append( " width=\"" ).append( wt ).append( "\"" );
    223        }
    224        if( alt != null ) {
    225            result.append( " alt=\"" ).append( alt ).append( "\"" );
    226        }
    227        if( border != null ) {
    228            result.append( " border=\"" ).append( border ).append( "\"" );
    229        }
    230        // if( map != null )    result.append(" map=\""+map+"\"");
    231
    232        result.append(" />");
    233        if( link != null ) {
    234            result.append("</a>");
    235        }
    236        result.append("</td></tr>\n");
    237        result.append("</table>\n");
    238
    239        return result.toString();
    240    }
    241
    242    private boolean validTargetValue( final String s ) {
    243        if( s.equals("_blank")
    244            || s.equals("_self")
    245            || s.equals("_parent")
    246            || s.equals("_top") ) {
    247            return true;
    248        } else if( !s.isEmpty() ) { // check [a-zA-z]
    249            final char c = s.charAt(0);
    250            return Character.isLowerCase(c) || Character.isUpperCase(c);
    251        }
    252        return false;
    253    }
    254
    255}