properties loaded from plugin
This commit is contained in:
		| @ -5,98 +5,104 @@ import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.util.StringUtils; | ||||
|  | ||||
| import java.io.ByteArrayOutputStream; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Files; | ||||
| import java.nio.file.Path; | ||||
| import java.util.ArrayList; | ||||
| import java.nio.file.StandardOpenOption; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import static org.owasp.webgoat.plugins.PluginFileUtils.fileEndsWith; | ||||
| import static org.owasp.webgoat.plugins.PluginFileUtils.hasParentDirectoryWithName; | ||||
|  | ||||
| public class Plugin { | ||||
|  | ||||
|     private static final Logger logger = LoggerFactory.getLogger(Plugin.class); | ||||
|     private final Class<AbstractLesson> lesson; | ||||
|     private static final String NAME_LESSON_SOLUTION_DIRECTORY = "lessonSolutions"; | ||||
|     private static final String NAME_LESSON_PLANS_DIRECTORY = "lessonPlans"; | ||||
|     private static final String NAME_LESSON_I18N_DIRECTORY = "i18n"; | ||||
|     private final Logger logger = LoggerFactory.getLogger(Plugin.class); | ||||
|     private final Path pluginDirectory; | ||||
|     private final Map<String, File> solutionLanguageFiles; | ||||
|     private final Map<String, File> lessonPlansLanguageFiles; | ||||
|     private final File lessonSourceFile; | ||||
|  | ||||
|     private Class<AbstractLesson> lesson; | ||||
|     private Map<String, File> solutionLanguageFiles = new HashMap<>(); | ||||
|     private Map<String, File> lessonPlansLanguageFiles = new HashMap<>(); | ||||
|     private File lessonSourceFile; | ||||
|  | ||||
|     public static class PluginLoadingFailure extends RuntimeException { | ||||
|  | ||||
|         public PluginLoadingFailure(String message) { | ||||
|             super(message); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public static class Builder { | ||||
|  | ||||
|         private Path pluginDirectory; | ||||
|         private Class lesson; | ||||
|         private final List<String> loadedClasses = new ArrayList<String>(); | ||||
|         private final Map<String, File> solutionLanguageFiles = new HashMap<>(); | ||||
|         private final Map<String, File> lessonPlansLanguageFiles = new HashMap<>(); | ||||
|         private File javaSource; | ||||
|  | ||||
|         public Builder loadClasses(Map<String, byte[]> classes) { | ||||
|             for (Map.Entry<String, byte[]> clazz : classes.entrySet() ) { | ||||
|                 loadClass(clazz.getKey(), clazz.getValue()); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder loadClass(String name, byte[] classFile) { | ||||
|             ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); | ||||
|             PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile); | ||||
|             try { | ||||
|                 String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); | ||||
|                 Class clazz = pluginClassLoader.loadClass(realClassName); | ||||
|                 if (AbstractLesson.class.isAssignableFrom(clazz)) { | ||||
|                     this.lesson = clazz; | ||||
|                 } | ||||
|                 loadedClasses.add(clazz.getName()); | ||||
|             } catch (ClassNotFoundException e) { | ||||
|                 logger.error("Unable to load class {}", name); | ||||
|             } | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Builder setBaseDirectory(Path pluginDirectory) { | ||||
|             this.pluginDirectory = pluginDirectory; | ||||
|             return this; | ||||
|         } | ||||
|  | ||||
|         public Plugin build() { | ||||
|             if ( lesson == null ) { | ||||
|                 throw new PluginLoadingFailure(String.format("Lesson class not found, following classes were detected in the plugin: %s", | ||||
|                     StringUtils.collectionToCommaDelimitedString(loadedClasses))); | ||||
|             } | ||||
|             return new Plugin(this.lesson, pluginDirectory, lessonPlansLanguageFiles, solutionLanguageFiles, javaSource); | ||||
|         } | ||||
|  | ||||
|         public void loadFiles(List<Path> files) { | ||||
|             for (Path file : files) { | ||||
|                 if (file.getFileName().toString().endsWith(".html") && file.getParent().getParent().getFileName().toString() | ||||
|                     .endsWith("lessonSolutions")) { | ||||
|                     solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); | ||||
|                 } | ||||
|                 if (file.getFileName().toString().endsWith(".html") && file.getParent().getParent().getFileName().toString() | ||||
|                     .endsWith("lessonPlans")) { | ||||
|                     lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); | ||||
|                 } | ||||
|                 if ( file.getFileName().toString().endsWith(".java")) { | ||||
|                     javaSource = file.toFile(); | ||||
|                 } | ||||
|             } | ||||
|         public PluginLoadingFailure(String message, Exception e) { | ||||
|             super(message, e); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Plugin(Class<AbstractLesson> lesson, Path pluginDirectory, Map<String, File> lessonPlansLanguageFiles, | ||||
|         Map<String, File> solutionLanguageFiles, File lessonSourceFile) { | ||||
|         this.lesson = lesson; | ||||
|     public Plugin(Path pluginDirectory) { | ||||
|         this.pluginDirectory = pluginDirectory; | ||||
|         this.lessonPlansLanguageFiles = lessonPlansLanguageFiles; | ||||
|         this.solutionLanguageFiles = solutionLanguageFiles; | ||||
|         this.lessonSourceFile = lessonSourceFile; | ||||
|     } | ||||
|  | ||||
|     public void loadClasses(Map<String, byte[]> classes) { | ||||
|         for (Map.Entry<String, byte[]> clazz : classes.entrySet()) { | ||||
|             loadClass(clazz.getKey(), clazz.getValue()); | ||||
|         } | ||||
|         if (lesson == null) { | ||||
|             throw new PluginLoadingFailure(String | ||||
|                 .format("Lesson class not found, following classes were detected in the plugin: %s", | ||||
|                     StringUtils.collectionToCommaDelimitedString(classes.keySet()))); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void loadClass(String name, byte[] classFile) { | ||||
|         ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); | ||||
|         PluginClassLoader pluginClassLoader = new PluginClassLoader(contextClassLoader, classFile); | ||||
|         try { | ||||
|             String realClassName = name.replaceFirst("/", "").replaceAll("/", ".").replaceAll(".class", ""); | ||||
|             Class clazz = pluginClassLoader.loadClass(realClassName); | ||||
|             if (AbstractLesson.class.isAssignableFrom(clazz)) { | ||||
|                 this.lesson = clazz; | ||||
|             } | ||||
|         } catch (ClassNotFoundException e) { | ||||
|             logger.error("Unable to load class {}", name); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void loadFiles(List<Path> files) { | ||||
|         for (Path file : files) { | ||||
|             if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_SOLUTION_DIRECTORY)) { | ||||
|                 solutionLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); | ||||
|             } | ||||
|             if (fileEndsWith(file, ".html") && hasParentDirectoryWithName(file, NAME_LESSON_PLANS_DIRECTORY)) { | ||||
|                 lessonPlansLanguageFiles.put(file.getParent().getFileName().toString(), file.toFile()); | ||||
|             } | ||||
|             if (fileEndsWith(file, ".java")) { | ||||
|                 lessonSourceFile = file.toFile(); | ||||
|             } | ||||
|             if (fileEndsWith(file, ".properties") && hasParentDirectoryWithName(file, NAME_LESSON_I18N_DIRECTORY)) { | ||||
|                 try { | ||||
|                     ByteArrayOutputStream bos = new ByteArrayOutputStream(); | ||||
|                     Files.copy(file, bos); | ||||
|                     Path propertiesPath = createPropertiesDirectory(); | ||||
|                     ResourceBundleClassLoader.setPropertiesPath(propertiesPath); | ||||
|                     Files.write(propertiesPath.resolve(file.getFileName()), bos.toByteArray(), | ||||
|                         StandardOpenOption.CREATE, StandardOpenOption.APPEND); | ||||
|                 } catch (IOException io) { | ||||
|                     throw new PluginLoadingFailure("Property file detected, but unable to copy the properties", io); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Path createPropertiesDirectory() throws IOException { | ||||
|         if (Files.exists(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY))) { | ||||
|             return pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY); | ||||
|         } else { | ||||
|             return Files.createDirectory(pluginDirectory.resolve(NAME_LESSON_I18N_DIRECTORY)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public Class<AbstractLesson> getLesson() { | ||||
| @ -107,7 +113,9 @@ public class Plugin { | ||||
|         return this.solutionLanguageFiles; | ||||
|     } | ||||
|  | ||||
|     public File getLessonSource() { return lessonSourceFile; } | ||||
|     public File getLessonSource() { | ||||
|         return lessonSourceFile; | ||||
|     } | ||||
|  | ||||
|     public Map<String, File> getLessonPlans() { | ||||
|         return this.lessonPlansLanguageFiles; | ||||
|  | ||||
							
								
								
									
										22
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/main/java/org/owasp/webgoat/plugins/PluginFileUtils.java
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
|  | ||||
| import java.nio.file.Path; | ||||
|  | ||||
| public class PluginFileUtils { | ||||
|  | ||||
|     public static boolean fileEndsWith(Path p, String s) { | ||||
|         return p.getFileName().toString().endsWith(s); | ||||
|     } | ||||
|  | ||||
|     public static boolean hasParentDirectoryWithName(Path p, String s) { | ||||
|         if (p == null || p.getParent() == null || p.getRoot().equals(p.getParent())) { | ||||
|             return false; | ||||
|         } | ||||
|         if (p.getParent().getFileName().toString().equals(s)) { | ||||
|             return true; | ||||
|         } | ||||
|         return hasParentDirectoryWithName(p.getParent(), s); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -31,11 +31,10 @@ public class PluginsLoader implements Runnable { | ||||
|                     try { | ||||
|                         PluginExtractor extractor = new PluginExtractor(file); | ||||
|                         extractor.extract(); | ||||
|                         Plugin.Builder builder = new Plugin.Builder(); | ||||
|                         builder.loadClasses(extractor.getClasses()); | ||||
|                         builder.loadFiles(extractor.getFiles()); | ||||
|                         builder.setBaseDirectory(extractor.getBaseDirectory()); | ||||
|                         plugins.add(builder.build()); | ||||
|                         Plugin plugin = new Plugin(extractor.getBaseDirectory()); | ||||
|                         plugin.loadClasses(extractor.getClasses()); | ||||
|                         plugin.loadFiles(extractor.getFiles()); | ||||
|                         plugins.add(plugin); | ||||
|                     } catch (Plugin.PluginLoadingFailure e) { | ||||
|                        logger.error("Unable to load plugin, continue reading others..."); | ||||
|                     } | ||||
|  | ||||
| @ -0,0 +1,33 @@ | ||||
| package org.owasp.webgoat.plugins; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.net.URL; | ||||
| import java.net.URLClassLoader; | ||||
| import java.nio.file.Path; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class ResourceBundleClassLoader { | ||||
|  | ||||
|     private final static ResourceBundleClassLoader classLoader = new ResourceBundleClassLoader(); | ||||
|     private Path propertiesPath; | ||||
|  | ||||
|     private ResourceBundleClassLoader() { | ||||
|     } | ||||
|  | ||||
|     public static void setPropertiesPath(Path path) { | ||||
|         classLoader.propertiesPath = path; | ||||
|     } | ||||
|  | ||||
|     public static ClassLoader createPropertyFilesClassLoader(ClassLoader parentClassLoader) { | ||||
|         final List<URL> urls = new ArrayList<>(); | ||||
|  | ||||
|         try { | ||||
|             urls.add(classLoader.propertiesPath.toUri().toURL()); | ||||
|         } catch (IOException e) { | ||||
|             throw new Plugin.PluginLoadingFailure("Unable to load the properties for the classloader", e); | ||||
|         } | ||||
|         return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread().getContextClassLoader()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; | ||||
| import javax.servlet.ServletContext; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.nio.file.Path; | ||||
| import java.nio.file.Paths; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| @ -313,7 +314,9 @@ public class Course { | ||||
|             logger.error("Plugins directory {} not found", path); | ||||
|             return; | ||||
|         } | ||||
|         List<Plugin> plugins = new PluginsLoader(Paths.get(path)).loadPlugins(); | ||||
|         Path pluginDirectory = Paths.get(path); | ||||
|         webgoatContext.setPluginDirectory(pluginDirectory); | ||||
|         List<Plugin> plugins = new PluginsLoader(pluginDirectory).loadPlugins(); | ||||
|         for (Plugin plugin : plugins) { | ||||
|             try { | ||||
|                 Class<AbstractLesson> c = plugin.getLesson(); | ||||
| @ -330,7 +333,7 @@ public class Course { | ||||
|                 for(Map.Entry<String, File> lessonPlan : plugin.getLessonPlans().entrySet()) { | ||||
|                     lesson.setLessonPlanFileName(lessonPlan.getKey(), lessonPlan.getValue().toString()); | ||||
|                 } | ||||
|                 lesson.setLessonSolutionFileName(plugin.getLessonPlans().get("en").toString()); | ||||
|                 lesson.setLessonSolutionFileName(plugin.getLessonSolutions().get("en").toString()); | ||||
|                 lesson.setSourceFileName(plugin.getLessonSource().toString()); | ||||
|             } catch (Exception e) { | ||||
|                 logger.error("Error in loadLessons: ", e); | ||||
| @ -433,4 +436,5 @@ public class Course { | ||||
|         //loadResources(); | ||||
|  | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,11 @@ | ||||
| package org.owasp.webgoat.session; | ||||
|  | ||||
| import javax.servlet.http.HttpServlet; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import javax.servlet.http.HttpServlet; | ||||
| import java.nio.file.Path; | ||||
|  | ||||
| public class WebgoatContext { | ||||
|  | ||||
|     final Logger logger = LoggerFactory.getLogger(WebgoatContext.class); | ||||
| @ -80,6 +82,8 @@ public class WebgoatContext { | ||||
|  | ||||
|     private String defaultLanguage; | ||||
|  | ||||
|     private java.nio.file.Path pluginDirectory; | ||||
|  | ||||
|     public WebgoatContext(HttpServlet servlet) { | ||||
|         this.servlet = servlet; | ||||
|         databaseConnectionString = getParameter(servlet, DATABASE_CONNECTION_STRING); | ||||
| @ -213,4 +217,12 @@ public class WebgoatContext { | ||||
|         return defaultLanguage; | ||||
|     } | ||||
|  | ||||
|     public Path getPluginDirectory() { | ||||
|         return pluginDirectory; | ||||
|     } | ||||
|  | ||||
|     public void setPluginDirectory(Path pluginDirectory) { | ||||
|         this.pluginDirectory = pluginDirectory; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
|  | ||||
| package org.owasp.webgoat.util; | ||||
|  | ||||
| import org.owasp.webgoat.plugins.ResourceBundleClassLoader; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| @ -38,7 +39,7 @@ import java.util.ResourceBundle; | ||||
| @Component | ||||
| public class LabelProvider | ||||
| { | ||||
| 	public final static String DEFAULT_LANGUAGE = "en"; | ||||
| 	public final static String DEFAULT_LANGUAGE = Locale.ENGLISH.getLanguage(); | ||||
|  | ||||
| 	private final HashMap<Locale, ResourceBundle> labels = new HashMap<Locale, ResourceBundle>(); | ||||
| 	private final WebGoatResourceBundleController localeController = new WebGoatResourceBundleController(); | ||||
| @ -47,7 +48,8 @@ public class LabelProvider | ||||
| 	{ | ||||
| 		if (!labels.containsKey(locale)) | ||||
| 		{ | ||||
| 			ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", locale, localeController); | ||||
| 			ClassLoader classLoader = ResourceBundleClassLoader.createPropertyFilesClassLoader(ResourceBundle.class.getClassLoader()); | ||||
| 			ResourceBundle resBundle = ResourceBundle.getBundle("WebGoatLabels", new Locale(DEFAULT_LANGUAGE), classLoader); | ||||
| 			labels.put(locale, resBundle); | ||||
| 		} | ||||
| 		return labels.get(locale).getString(strName); | ||||
|  | ||||
| @ -1,21 +0,0 @@ | ||||
| #General | ||||
| LessonCompleted=Congratulations. You have successfully completed this lesson. | ||||
| RestartLesson=Restart this Lesson | ||||
| SolutionVideos=Solution Videos | ||||
| ErrorGenerating=Error generating | ||||
| InvalidData=Invalid Data  | ||||
| Go!=Go! | ||||
|  | ||||
|  | ||||
| #StringSqlInjection.java | ||||
| StringSqlInjectionSecondStage=Now that you have successfully performed an SQL injection, try the same type of attack on a parameterized query.  Restart the lesson if you wish to return to the injectable query. | ||||
| EnterLastName=Enter your last name: | ||||
| NoResultsMatched=No results matched.  Try Again. | ||||
| SqlStringInjectionHint1=The application is taking your input and inserting it at the end of a pre-formed SQL command. | ||||
| SqlStringInjectionHint2=This is the code for the query being built and issued by WebGoat:<br><br> "SELECT * FROM user_data WHERE last_name = "accountName" | ||||
| SqlStringInjectionHint3=Compound SQL statements can be made by joining multiple tests with keywords like AND and OR. Try appending a SQL statement that always resolves to true | ||||
| SqlStringInjectionHint4=Try entering [ smith' OR '1' = '1 ]. | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| #General | ||||
| LessonCompleted=Herzlichen Gl\u00fcckwunsch! Sie haben diese Lektion erfolgreich abgeschlossen. | ||||
| RestartLesson=Lektion neu beginnen | ||||
| SolutionVideos=L\u00f6sungsvideos | ||||
| ErrorGenerating=Fehler beim Generieren von | ||||
| InvalidData=Ung\u00fcltige Daten | ||||
|  | ||||
|   | ||||
| @ -1,7 +0,0 @@ | ||||
| #General | ||||
| LessonCompleted=F\u00e9licitations. Vous avez termin\u00e9 cette le\u00e7on avec succ\u00e9s. | ||||
| RestartLesson=Recommencer cette le\u00e7on | ||||
| SolutionVideos=Solution vid\u00e9os | ||||
| ErrorGenerating=Error generating | ||||
| InvalidData=Donn\u00e9e invalide  | ||||
|  | ||||
| @ -1,7 +0,0 @@ | ||||
| #General | ||||
| LessonCompleted=\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u044e. \u0412\u044b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043f\u0440\u043e\u0448\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0439 \u0443\u0440\u043e\u043a. | ||||
| RestartLesson=\u041d\u0430\u0447\u0430\u043b\u044c \u0441\u043d\u0430\u0447\u0430\u043b\u0430 | ||||
| SolutionVideos=\u0412\u0438\u0434\u0435\u043e \u0441 \u0440\u0435\u0448\u0435\u043d\u0438\u0435\u043c | ||||
| ErrorGenerating=\u041f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0430 \u043e\u0448\u0438\u0431\u043a\u0430 | ||||
| InvalidData=\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 | ||||
|  | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -1 +1 @@ | ||||
| ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n	<%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n	${pageContext.request.contextPath}\nsnippet cout\n	<c:out value="${1}" default="${2}" />\nsnippet cset\n	<c:set var="${1}" value="${2}" />\nsnippet cremove\n	<c:remove var="${1}" scope="${2:page}" />\nsnippet ccatch\n	<c:catch var="${1}" />\nsnippet cif\n	<c:if test="${${1}}">\n		${2}\n	</c:if>\nsnippet cchoose\n	<c:choose>\n		${1}\n	</c:choose>\nsnippet cwhen\n	<c:when test="${${1}}">\n		${2}\n	</c:when>\nsnippet cother\n	<c:otherwise>\n		${1}\n	</c:otherwise>\nsnippet cfore\n	<c:forEach items="${${1}}" var="${2}" varStatus="${3}">\n		${4:<c:out value="$2" />}\n	</c:forEach>\nsnippet cfort\n	<c:set var="${1}">${2:item1,item2,item3}</c:set>\n	<c:forTokens var="${3}" items="${$1}" delims="${4:,}">\n		${5:<c:out value="$3" />}\n	</c:forTokens>\nsnippet cparam\n	<c:param name="${1}" value="${2}" />\nsnippet cparam+\n	<c:param name="${1}" value="${2}" />\n	cparam+${3}\nsnippet cimport\n	<c:import url="${1}" />\nsnippet cimport+\n	<c:import url="${1}">\n		<c:param name="${2}" value="${3}" />\n		cparam+${4}\n	</c:import>\nsnippet curl\n	<c:url value="${1}" var="${2}" />\n	<a href="${$2}">${3}</a>\nsnippet curl+\n	<c:url value="${1}" var="${2}">\n		<c:param name="${4}" value="${5}" />\n		cparam+${6}\n	</c:url>\n	<a href="${$2}">${3}</a>\nsnippet credirect\n	<c:redirect url="${1}" />\nsnippet contains\n	${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n	${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n	${fn:endsWith(${1:string}, ${2:suffix})}\nsnippet escape\n	${fn:escapeXml(${1:string})}\nsnippet indexof\n	${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n	${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n	${fn:length(${1:collection_or_string})}\nsnippet replace\n	${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n	${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n	${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n	${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n	${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n	${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n	${fn:toLowerCase(${1:string})}\nsnippet uc\n	${fn:toUpperCase(${1:string})}\nsnippet trim\n	${fn:trim(${1:string})}\n',t.scope="jsp"}) | ||||
| ace.define("ace/snippets/jsp",["require","exports","module"],function(e,t,n){"use strict";t.snippetText='snippet @page\n	<%@page contentType="text/html" pageEncoding="UTF-8"%>\nsnippet jstl\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet jstl:c\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>\nsnippet jstl:fn\n	<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>\nsnippet cpath\n	${pageContext.request.contextPath}\nsnippet cout\n	<c:out value="${1}" default="${2}" />\nsnippet cset\n	<c:set var="${1}" value="${2}" />\nsnippet cremove\n	<c:remove var="${1}" scope="${2:page}" />\nsnippet ccatch\n	<c:catch var="${1}" />\nsnippet cif\n	<c:if test="${${1}}">\n		${2}\n	</c:if>\nsnippet cchoose\n	<c:choose>\n		${1}\n	</c:choose>\nsnippet cwhen\n	<c:when test="${${1}}">\n		${2}\n	</c:when>\nsnippet cother\n	<c:otherwise>\n		${1}\n	</c:otherwise>\nsnippet cfore\n	<c:forEach items="${${1}}" var="${2}" varStatus="${3}">\n		${4:<c:out value="$2" />}\n	</c:forEach>\nsnippet cfort\n	<c:set var="${1}">${2:item1,item2,item3}</c:set>\n	<c:forTokens var="${3}" items="${$1}" delims="${4:,}">\n		${5:<c:out value="$3" />}\n	</c:forTokens>\nsnippet cparam\n	<c:param name="${1}" value="${2}" />\nsnippet cparam+\n	<c:param name="${1}" value="${2}" />\n	cparam+${3}\nsnippet cimport\n	<c:import url="${1}" />\nsnippet cimport+\n	<c:import url="${1}">\n		<c:param name="${2}" value="${3}" />\n		cparam+${4}\n	</c:import>\nsnippet curl\n	<c:url value="${1}" var="${2}" />\n	<a href="${$2}">${3}</a>\nsnippet curl+\n	<c:url value="${1}" var="${2}">\n		<c:param name="${4}" value="${5}" />\n		cparam+${6}\n	</c:url>\n	<a href="${$2}">${3}</a>\nsnippet credirect\n	<c:redirect url="${1}" />\nsnippet contains\n	${fn:contains(${1:string}, ${2:substr})}\nsnippet contains:i\n	${fn:containsIgnoreCase(${1:string}, ${2:substr})}\nsnippet endswith\n	${fn:fileEndsWith(${1:string}, ${2:suffix})}\nsnippet escape\n	${fn:escapeXml(${1:string})}\nsnippet indexof\n	${fn:indexOf(${1:string}, ${2:substr})}\nsnippet join\n	${fn:join(${1:collection}, ${2:delims})}\nsnippet length\n	${fn:length(${1:collection_or_string})}\nsnippet replace\n	${fn:replace(${1:string}, ${2:substr}, ${3:replace})}\nsnippet split\n	${fn:split(${1:string}, ${2:delims})}\nsnippet startswith\n	${fn:startsWith(${1:string}, ${2:prefix})}\nsnippet substr\n	${fn:substring(${1:string}, ${2:begin}, ${3:end})}\nsnippet substr:a\n	${fn:substringAfter(${1:string}, ${2:substr})}\nsnippet substr:b\n	${fn:substringBefore(${1:string}, ${2:substr})}\nsnippet lc\n	${fn:toLowerCase(${1:string})}\nsnippet uc\n	${fn:toUpperCase(${1:string})}\nsnippet trim\n	${fn:trim(${1:string})}\n',t.scope="jsp"}) | ||||
		Reference in New Issue
	
	Block a user