View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.view.swing.interaction;
12  
13  import com.jsql.util.I18nUtil;
14  import com.jsql.util.LogLevelUtil;
15  import com.jsql.view.interaction.InteractionCommand;
16  import com.jsql.view.swing.tab.TabHeader;
17  import com.jsql.view.swing.util.I18nViewUtil;
18  import com.jsql.view.swing.util.MediatorHelper;
19  import com.jsql.view.swing.util.UiUtil;
20  import org.apache.commons.lang3.StringUtils;
21  import org.apache.logging.log4j.LogManager;
22  import org.apache.logging.log4j.Logger;
23  import org.jsoup.Jsoup;
24  import org.jsoup.safety.Safelist;
25  
26  import javax.swing.*;
27  import javax.swing.text.DefaultEditorKit;
28  import java.awt.*;
29  import java.awt.datatransfer.StringSelection;
30  import java.awt.event.*;
31  import java.io.IOException;
32  import java.util.EmptyStackException;
33  
34  /**
35   * Create a new tab for an administration webpage.
36   */
37  public class CreateAdminPageTab extends CreateTabHelper implements InteractionCommand {
38      
39      private static final Logger LOGGER = LogManager.getRootLogger();
40  
41      /**
42       * Url for the administration webpage.
43       */
44      private final String url;
45  
46      /**
47       * @param interactionParams Url of the webpage
48       */
49      public CreateAdminPageTab(Object[] interactionParams) {
50          this.url = (String) interactionParams[0];
51      }
52  
53      @Override
54      public void execute() {
55          String htmlSource = StringUtils.EMPTY;
56          
57          // Fix #4081: SocketTimeoutException on get()
58          // Fix #44642: NoClassDefFoundError on get()
59          // Fix #44641: ExceptionInInitializerError on get()
60          try {
61              // Previous test for 2xx Success and 3xx Redirection was Header only,
62              // now get the HTML content.
63              // Proxy is used by jsoup
64              htmlSource = Jsoup.clean(
65                  Jsoup.connect(this.url)
66                      // Prevent exception on UnsupportedMimeTypeException: Unhandled content type. Must be text/*, application/xml, or application/*+xml
67                      .ignoreContentType(true)
68                      // Prevent exception on HTTP errors
69                      .ignoreHttpErrors(true)
70                      .get()
71                      .html()
72                      .replaceAll("<img[^>]*>", StringUtils.EMPTY)
73                      .replaceAll("<input[^>]*type=\"?hidden\"?[^>]*>", StringUtils.EMPTY)
74                      .replaceAll("<input[^>]*type=\"?(submit|button)\"?[^>]*>", "<div style=\"background-color:#eeeeee;text-align:center;border:1px solid black;width:100px;\">button</div>")
75                      .replaceAll("<input[^>]*>", "<div style=\"text-align:center;border:1px solid black;width:100px;\">input</div>"),
76                  Safelist.relaxed()
77                      .addTags("center", "div", "span")
78                      .addAttributes(":all", "style")
79              );
80          } catch (IOException e) {
81              LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Failure opening page: {}", e.getMessage());
82          } catch (ExceptionInInitializerError | NoClassDefFoundError e) {
83              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
84          }
85  
86          final var browser = new JTextPane();
87          browser.setContentType("text/html");
88          browser.setEditable(false);
89          browser.setCaretPosition(0);
90  
91          // Fix #43220: EmptyStackException on setText()
92          // Fix #94242: IndexOutOfBoundsException on setText()
93          try {
94              browser.setText(htmlSource);
95          } catch (IndexOutOfBoundsException | EmptyStackException e) {
96              LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
97          }
98  
99          JMenuItem itemCopyUrl = new JMenuItem(I18nUtil.valueByKey("CONTEXT_MENU_COPY_PAGE_URL"));
100         I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY_PAGE_URL", itemCopyUrl);
101 
102         JMenuItem itemCopy = new JMenuItem();
103         itemCopy.setAction(browser.getActionMap().get(DefaultEditorKit.copyAction));
104         itemCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
105         itemCopy.setMnemonic('C');
106         itemCopy.setText(I18nUtil.valueByKey("CONTEXT_MENU_COPY"));
107         I18nViewUtil.addComponentForKey("CONTEXT_MENU_COPY", itemCopy);
108 
109         JMenuItem itemSelectAll = new JMenuItem();
110         itemSelectAll.setAction(browser.getActionMap().get(DefaultEditorKit.selectAllAction));
111         itemSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
112         itemSelectAll.setText(I18nUtil.valueByKey("CONTEXT_MENU_SELECT_ALL"));
113         I18nViewUtil.addComponentForKey("CONTEXT_MENU_SELECT_ALL", itemSelectAll);
114         itemSelectAll.setMnemonic('A');
115 
116         final var menu = new JPopupMenu();
117         menu.add(itemCopyUrl);
118         menu.add(new JSeparator());
119         menu.add(itemCopy);
120         menu.add(itemSelectAll);
121         menu.applyComponentOrientation(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()));
122 
123         itemCopyUrl.addActionListener(actionEvent -> {
124             var stringSelection = new StringSelection(this.url);
125             var clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
126             clipboard.setContents(stringSelection, null);
127         });
128 
129         itemSelectAll.addActionListener(actionEvent -> browser.selectAll());
130         
131         browser.addFocusListener(new FocusAdapter() {
132             @Override
133             public void focusGained(FocusEvent focusEvent) {
134                 browser.getCaret().setVisible(true);
135                 browser.getCaret().setSelectionVisible(true);
136             }
137         });
138         
139         browser.addMouseListener(new MouseAdapter() {
140             @Override
141             public void mousePressed(MouseEvent evt) {
142                 browser.requestFocusInWindow();
143                 if (evt.isPopupTrigger()) {
144                     menu.show(evt.getComponent(), evt.getX(), evt.getY());
145                 }
146             }
147             @Override
148             public void mouseReleased(MouseEvent evt) {
149                 if (evt.isPopupTrigger()) {
150                     // Fix #45348: IllegalComponentStateException on show()
151                     try {
152                         menu.show(evt.getComponent(), evt.getX(), evt.getY());
153                     } catch (IllegalComponentStateException e) {
154                         LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
155                     }
156                     menu.setLocation(
157                         ComponentOrientation.RIGHT_TO_LEFT.equals(ComponentOrientation.getOrientation(I18nUtil.getCurrentLocale()))
158                         ? evt.getXOnScreen() - menu.getWidth()
159                         : evt.getXOnScreen(),
160                         evt.getYOnScreen()
161                     );
162                 }
163             }
164         });
165 
166         final var scroller = new JScrollPane(browser);
167         MediatorHelper.tabResults().addTab(this.url.replaceAll(".*/", StringUtils.EMPTY) + StringUtils.SPACE, scroller);
168         try {  // Fix #96175: ArrayIndexOutOfBoundsException on setSelectedComponent()
169             MediatorHelper.tabResults().setSelectedComponent(scroller);  // Focus on the new tab
170         } catch (ArrayIndexOutOfBoundsException e) {
171             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
172         }
173         MediatorHelper.tabResults().setToolTipTextAt(
174             MediatorHelper.tabResults().indexOfComponent(scroller),
175             String.format("<html>%s</html>", this.url)
176         );
177 
178         // Create a custom tab header
179         var header = new TabHeader(
180             this.url.replaceAll(".*/", StringUtils.EMPTY),
181             UiUtil.ADMIN.getIcon()
182         );
183         MediatorHelper.tabResults().setTabComponentAt(MediatorHelper.tabResults().indexOfComponent(scroller), header);  // Apply the custom header to the tab
184         browser.setCaretPosition(0);
185 
186         MediatorHelper.tabResults().updateUI();  // required: light, open/close prefs, dark => light artifacts
187     }
188 }