mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
manifest editing in artifacts improved: IDEADEV-40906, IDEADEV-40727, IDEADEV-40713, IDEADEV-40709, IDEADEV-40712
This commit is contained in:
@@ -152,7 +152,8 @@ public class ArtifactModelImpl extends ArtifactModelBase implements ModifiableAr
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ArtifactImpl getModifiableCopy(ArtifactImpl artifact) {
|
||||
public ArtifactImpl getModifiableCopy(Artifact artifact) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
return myArtifact2ModifiableCopy.get(artifact);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.intellij.packaging.impl.artifacts;
|
||||
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.CompilerProjectExtension;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.SourceFolder;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
@@ -355,56 +358,55 @@ public class ArtifactUtil {
|
||||
return files.isEmpty() ? null : files.get(0);
|
||||
}
|
||||
|
||||
public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, String outputPath,
|
||||
PackagingElementResolvingContext context, ArtifactType artifactType) {
|
||||
outputPath = StringUtil.trimStart(outputPath, "/");
|
||||
if (outputPath.length() == 0) {
|
||||
public static List<VirtualFile> findSourceFilesByOutputPath(CompositePackagingElement<?> parent, final String outputPath,
|
||||
final PackagingElementResolvingContext context, final ArtifactType artifactType) {
|
||||
final String path = StringUtil.trimStart(outputPath, "/");
|
||||
if (path.length() == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
int i = outputPath.indexOf('/');
|
||||
final String firstName = i != -1 ? outputPath.substring(0, i) : outputPath;
|
||||
String tail = i != -1 ? outputPath.substring(i+1) : "";
|
||||
int i = path.indexOf('/');
|
||||
final String firstName = i != -1 ? path.substring(0, i) : path;
|
||||
final String tail = i != -1 ? path.substring(i+1) : "";
|
||||
|
||||
final List<CompositePackagingElement<?>> compositeChildren = new SmartList<CompositePackagingElement<?>>();
|
||||
final List<FileCopyPackagingElement> fileCopies = new SmartList<FileCopyPackagingElement>();
|
||||
final List<DirectoryCopyPackagingElement> dirCopies = new SmartList<DirectoryCopyPackagingElement>();
|
||||
final List<VirtualFile> result = new SmartList<VirtualFile>();
|
||||
processElements(parent.getChildren(), context, artifactType, new Processor<PackagingElement<?>>() {
|
||||
public boolean process(PackagingElement<?> element) {
|
||||
//todo[nik] replace by method findSourceFile() in PackagingElement
|
||||
if (element instanceof CompositePackagingElement) {
|
||||
final CompositePackagingElement<?> compositeElement = (CompositePackagingElement<?>)element;
|
||||
if (firstName.equals(compositeElement.getName())) {
|
||||
compositeChildren.add(compositeElement);
|
||||
result.addAll(findSourceFilesByOutputPath(compositeElement, tail, context, artifactType));
|
||||
}
|
||||
}
|
||||
else if (element instanceof FileCopyPackagingElement) {
|
||||
final FileCopyPackagingElement fileCopyElement = (FileCopyPackagingElement)element;
|
||||
if (firstName.equals(fileCopyElement.getOutputFileName())) {
|
||||
fileCopies.add(fileCopyElement);
|
||||
if (firstName.equals(fileCopyElement.getOutputFileName()) && tail.length() == 0) {
|
||||
ContainerUtil.addIfNotNull(fileCopyElement.findFile(), result);
|
||||
}
|
||||
}
|
||||
else if (element instanceof DirectoryCopyPackagingElement) {
|
||||
dirCopies.add((DirectoryCopyPackagingElement)element);
|
||||
final VirtualFile sourceRoot = ((DirectoryCopyPackagingElement)element).findFile();
|
||||
if (sourceRoot != null) {
|
||||
ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
|
||||
}
|
||||
}
|
||||
else if (element instanceof ModuleOutputPackagingElement) {
|
||||
final Module module = ((ModuleOutputPackagingElement)element).findModule(context);
|
||||
final ContentEntry[] contentEntries = context.getModulesProvider().getRootModel(module).getContentEntries();
|
||||
for (ContentEntry contentEntry : contentEntries) {
|
||||
for (SourceFolder sourceFolder : contentEntry.getSourceFolders()) {
|
||||
final VirtualFile sourceRoot = sourceFolder.getFile();
|
||||
if (!sourceFolder.isTestSource() && sourceRoot != null) {
|
||||
ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(path), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
List<VirtualFile> result = new SmartList<VirtualFile>();
|
||||
for (CompositePackagingElement<?> child : compositeChildren) {
|
||||
result.addAll(findSourceFilesByOutputPath(child, tail, context, artifactType));
|
||||
}
|
||||
if (tail.length() == 0) {
|
||||
for (FileCopyPackagingElement fileCopy : fileCopies) {
|
||||
ContainerUtil.addIfNotNull(fileCopy.findFile(), result);
|
||||
}
|
||||
}
|
||||
for (DirectoryCopyPackagingElement dirCopy : dirCopies) {
|
||||
final VirtualFile sourceRoot = dirCopy.findFile();
|
||||
if (sourceRoot != null) {
|
||||
ContainerUtil.addIfNotNull(sourceRoot.findFileByRelativePath(outputPath), result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class FileCopyPackagingElement extends FileOrDirectoryCopyPackagingElemen
|
||||
}
|
||||
|
||||
public PackagingElementPresentation createPresentation(@NotNull ArtifactEditorContext context) {
|
||||
return new FileCopyPresentation(myFilePath, getOutputFileName());
|
||||
return new FileCopyPresentation(myFilePath, getOutputFileName(), context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.artifacts.ArtifactType;
|
||||
import com.intellij.packaging.elements.CompositePackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElementFactory;
|
||||
import com.intellij.packaging.elements.PackagingElementResolvingContext;
|
||||
import com.intellij.packaging.impl.artifacts.ArtifactUtil;
|
||||
import com.intellij.packaging.impl.artifacts.PackagingElementProcessor;
|
||||
@@ -55,16 +54,19 @@ public class ManifestFileUtil {
|
||||
public static final String MANIFEST_FILE_NAME = PathUtil.getFileName(MANIFEST_PATH);
|
||||
public static final String MANIFEST_DIR_NAME = PathUtil.getParentPath(MANIFEST_PATH);
|
||||
|
||||
private ManifestFileUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static VirtualFile findManifestFile(@NotNull CompositePackagingElement<?> root, PackagingElementResolvingContext context, ArtifactType artifactType) {
|
||||
return ArtifactUtil.findSourceFileByOutputPath(root, MANIFEST_PATH, context, artifactType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String suggestManifestFilePathAndAddElement(@NotNull CompositePackagingElement<?> root, PackagingElementResolvingContext context, ArtifactType artifactType) {
|
||||
@Nullable
|
||||
public static VirtualFile suggestManifestFileDirectory(@NotNull CompositePackagingElement<?> root, PackagingElementResolvingContext context, ArtifactType artifactType) {
|
||||
final VirtualFile metaInfDir = ArtifactUtil.findSourceFileByOutputPath(root, MANIFEST_DIR_NAME, context, artifactType);
|
||||
if (metaInfDir != null) {
|
||||
return metaInfDir.getPath() + "/" + MANIFEST_FILE_NAME;
|
||||
return metaInfDir;
|
||||
}
|
||||
|
||||
final Ref<VirtualFile> sourceDir = Ref.create(null);
|
||||
@@ -89,17 +91,15 @@ public class ManifestFileUtil {
|
||||
});
|
||||
|
||||
if (!sourceDir.isNull()) {
|
||||
return sourceDir.get().getPath() + "/" + MANIFEST_PATH;
|
||||
return sourceDir.get();
|
||||
}
|
||||
|
||||
|
||||
final Project project = context.getProject();
|
||||
final VirtualFile dir = suggestBaseDir(project, sourceFile.get());
|
||||
String filePath = dir.getPath() + "/" + MANIFEST_PATH;
|
||||
PackagingElementFactory.getInstance().addFileCopy(root, MANIFEST_DIR_NAME, filePath);
|
||||
return filePath;
|
||||
return suggestBaseDir(project, sourceFile.get());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static VirtualFile suggestBaseDir(Project project, final @Nullable VirtualFile file) {
|
||||
final VirtualFile[] contentRoots = ProjectRootManager.getInstance(project).getContentRoots();
|
||||
if (file == null && contentRoots.length > 0) {
|
||||
@@ -189,7 +189,11 @@ public class ManifestFileUtil {
|
||||
@NotNull
|
||||
public static ManifestFileConfiguration createManifestFileConfiguration(CompositePackagingElement<?> element,
|
||||
final PackagingElementResolvingContext context, final ArtifactType artifactType) {
|
||||
final VirtualFile manifestFile = findManifestFile(element, context, artifactType);
|
||||
return createManifestFileConfiguration(findManifestFile(element, context, artifactType));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ManifestFileConfiguration createManifestFileConfiguration(@Nullable VirtualFile manifestFile) {
|
||||
final List<String> classpath = new ArrayList<String>();
|
||||
String mainClass = null;
|
||||
final String path;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.impl.elements.PackagingElementFactoryImpl;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.packaging.ui.PackagingElementPresentation;
|
||||
import com.intellij.packaging.ui.PackagingElementWeights;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
@@ -32,10 +33,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class FileCopyPresentation extends PackagingElementPresentation {
|
||||
private final String mySourcePath;
|
||||
private final String myOutputFileName;
|
||||
private final ArtifactEditorContext myContext;
|
||||
private final VirtualFile myFile;
|
||||
|
||||
public FileCopyPresentation(String filePath, String outputFileName) {
|
||||
public FileCopyPresentation(String filePath, String outputFileName, ArtifactEditorContext context) {
|
||||
myOutputFileName = outputFileName;
|
||||
myContext = context;
|
||||
|
||||
String parentPath;
|
||||
myFile = LocalFileSystem.getInstance().findFileByPath(filePath);
|
||||
@@ -61,8 +64,8 @@ public class FileCopyPresentation extends PackagingElementPresentation {
|
||||
}
|
||||
|
||||
public void render(@NotNull PresentationData presentationData, SimpleTextAttributes mainAttributes, SimpleTextAttributes commentAttributes) {
|
||||
if (myFile != null && !myFile.isDirectory()) {
|
||||
presentationData.setIcons(myFile.getIcon());
|
||||
if (myFile != null && !myFile.isDirectory() || myContext.isManifestFile(mySourcePath)) {
|
||||
presentationData.setIcons(myFile != null ? myFile.getIcon() : PackagingElementFactoryImpl.FileCopyElementType.ICON);
|
||||
presentationData.addText(myOutputFileName, mainAttributes);
|
||||
presentationData.addText(" (" + mySourcePath + ")", commentAttributes);
|
||||
}
|
||||
|
||||
+140
-49
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.intellij.packaging.impl.ui.properties.ElementWithManifestPropertiesPanel">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="5" left="5" bottom="5" right="5"/>
|
||||
<constraints>
|
||||
<xy x="20" y="20" width="500" height="400"/>
|
||||
@@ -8,61 +8,152 @@
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="9a3d6" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="label.text.main.class"/>
|
||||
</properties>
|
||||
</component>
|
||||
<vspacer id="e5716">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="d87aa" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myMainClassField">
|
||||
<grid id="98ebe" binding="myPropertiesPanel" layout-manager="CardLayout" hgap="0" vgap="0">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="40600" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="label.text.class.path"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a5121" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myClasspathField">
|
||||
<constraints>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="e12c" class="javax.swing.JLabel" binding="myTitleLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="element name"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="446b5" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Manifest &File:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="52135" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myManifestFilePathField">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="e398c" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<card name="properties"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="9a3d6" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="label.text.main.class"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="d87aa" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myMainClassField">
|
||||
<constraints>
|
||||
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="40600" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/CompilerBundle" key="label.text.class.path"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a5121" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myClasspathField">
|
||||
<constraints>
|
||||
<grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
</component>
|
||||
<component id="e12c" class="javax.swing.JLabel" binding="myTitleLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="element name"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="446b5" class="javax.swing.JLabel">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="aca67"/>
|
||||
<text value="Manifest &File:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<grid id="599de" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="12e45" class="javax.swing.JButton" binding="myRemoveFromArtifactButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Remove from Artifact"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="7cf11">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
</children>
|
||||
</grid>
|
||||
<component id="aca67" class="javax.swing.JTextField" binding="myManifestPathField">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||
<preferred-size width="150" height="-1"/>
|
||||
</grid>
|
||||
</constraints>
|
||||
<properties>
|
||||
<editable value="false"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<grid id="4473f" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<card name="buttons"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="66e62" class="javax.swing.JButton" binding="myCreateManifestButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Create Manifest..."/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="970f7">
|
||||
<constraints>
|
||||
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<vspacer id="bf917">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<component id="c6af2" class="javax.swing.JButton" binding="myUseExistingManifestButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="&Use Existing Manifest..."/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="a0a7b" class="javax.swing.JLabel" binding="myManifestNotFoundLabel">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Manifest.mf file not found"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</form>
|
||||
|
||||
+119
-26
@@ -15,21 +15,29 @@
|
||||
*/
|
||||
package com.intellij.packaging.impl.ui.properties;
|
||||
|
||||
import com.intellij.CommonBundle;
|
||||
import com.intellij.ide.util.TreeClassChooser;
|
||||
import com.intellij.ide.util.TreeClassChooserFactory;
|
||||
import com.intellij.openapi.application.Result;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.packaging.elements.PackagingElementFactory;
|
||||
import com.intellij.packaging.impl.elements.CompositeElementWithManifest;
|
||||
import com.intellij.packaging.impl.elements.ManifestFileUtil;
|
||||
import com.intellij.packaging.ui.ArtifactEditorContext;
|
||||
import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
|
||||
import com.intellij.packaging.ui.ManifestFileConfiguration;
|
||||
import com.intellij.packaging.ui.PackagingElementPropertiesPanel;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
@@ -40,21 +48,29 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
public abstract class ElementWithManifestPropertiesPanel<E extends CompositeElementWithManifest<?>> extends PackagingElementPropertiesPanel {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.packaging.impl.ui.properties.ElementWithManifestPropertiesPanel");
|
||||
private final E myElement;
|
||||
private final ArtifactEditorContext myContext;
|
||||
private JPanel myMainPanel;
|
||||
private TextFieldWithBrowseButton myMainClassField;
|
||||
private TextFieldWithBrowseButton myClasspathField;
|
||||
private JLabel myTitleLabel;
|
||||
private TextFieldWithBrowseButton myManifestFilePathField;
|
||||
private JButton myRemoveFromArtifactButton;
|
||||
private JButton myCreateManifestButton;
|
||||
private JButton myUseExistingManifestButton;
|
||||
private JPanel myPropertiesPanel;
|
||||
private JTextField myManifestPathField;
|
||||
private JLabel myManifestNotFoundLabel;
|
||||
private ManifestFileConfiguration myManifestFileConfiguration;
|
||||
|
||||
public ElementWithManifestPropertiesPanel(E element, final ArtifactEditorContext context) {
|
||||
@@ -78,46 +94,123 @@ public abstract class ElementWithManifestPropertiesPanel<E extends CompositeElem
|
||||
}
|
||||
});
|
||||
|
||||
myMainClassField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
createManifestFileIfNeeded();
|
||||
}
|
||||
});
|
||||
myClasspathField.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
Messages.showTextAreaDialog(myClasspathField.getTextField(), "Edit Classpath", "classpath-attribute-editor");
|
||||
}
|
||||
});
|
||||
myManifestFilePathField.addBrowseFolderListener("Specify Path to MANIFEST.MF file", "", context.getProject(), new FileChooserDescriptor(true, false, false, false, false, false) {
|
||||
@Override
|
||||
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
|
||||
return super.isFileVisible(file, showHiddenFiles) && file.isDirectory() || file.getName().equalsIgnoreCase(ManifestFileUtil.MANIFEST_FILE_NAME);
|
||||
}
|
||||
});
|
||||
myClasspathField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
@Override
|
||||
protected void textChanged(DocumentEvent e) {
|
||||
createManifestFileIfNeeded();
|
||||
myContext.queueValidation();
|
||||
}
|
||||
});
|
||||
myUseExistingManifestButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
chooseManifest();
|
||||
}
|
||||
});
|
||||
myCreateManifestButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
createManifest();
|
||||
}
|
||||
});
|
||||
|
||||
//todo[nik] do we really need this button?
|
||||
myRemoveFromArtifactButton.setVisible(false);
|
||||
myRemoveFromArtifactButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
removeManifest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createManifestFileIfNeeded() {
|
||||
if ((myClasspathField.getText().trim().length() > 0 || myMainClassField.getText().trim().length() > 0)
|
||||
&& myManifestFilePathField.getText().length() == 0) {
|
||||
final String path = ManifestFileUtil.suggestManifestFilePathAndAddElement(myElement, myContext, myContext.getArtifactType());
|
||||
myManifestFilePathField.setText(FileUtil.toSystemDependentName(path));
|
||||
private void removeManifest() {
|
||||
}
|
||||
|
||||
private void createManifest() {
|
||||
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
descriptor.setTitle("Select Directory for MANIFEST.MF file");
|
||||
final VirtualFile[] files = FileChooser.chooseFiles(myContext.getProject(), descriptor, ManifestFileUtil.suggestManifestFileDirectory(myElement, myContext, myContext.getArtifactType()));
|
||||
if (files.length != 1) return;
|
||||
|
||||
final Ref<IOException> exc = Ref.create(null);
|
||||
final VirtualFile file = new WriteAction<VirtualFile>() {
|
||||
protected void run(final Result<VirtualFile> result) {
|
||||
VirtualFile dir = files[0];
|
||||
try {
|
||||
if (!dir.getName().equals(ManifestFileUtil.MANIFEST_DIR_NAME)) {
|
||||
VirtualFile newDir = dir.findChild(ManifestFileUtil.MANIFEST_DIR_NAME);
|
||||
if (newDir == null) {
|
||||
newDir = dir.createChildDirectory(this, ManifestFileUtil.MANIFEST_DIR_NAME);
|
||||
}
|
||||
dir = newDir;
|
||||
}
|
||||
result.setResult(dir.createChildData(this, ManifestFileUtil.MANIFEST_FILE_NAME));
|
||||
}
|
||||
catch (IOException e) {
|
||||
exc.set(e);
|
||||
}
|
||||
}
|
||||
}.execute().getResultObject();
|
||||
|
||||
final IOException exception = exc.get();
|
||||
if (exception != null) {
|
||||
LOG.info(exception);
|
||||
Messages.showErrorDialog(myMainPanel, exception.getMessage(), CommonBundle.getErrorTitle());
|
||||
return;
|
||||
}
|
||||
|
||||
PackagingElementFactory.getInstance().addFileCopy(myElement, ManifestFileUtil.MANIFEST_DIR_NAME, file.getPath());
|
||||
myContext.getThisArtifactEditor().updateLayoutTree();
|
||||
updateComponents(new ManifestFileConfiguration(null, null, file.getPath()));
|
||||
apply();
|
||||
}
|
||||
|
||||
private void chooseManifest() {
|
||||
final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, false, false, false, false) {
|
||||
@Override
|
||||
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
|
||||
return super.isFileVisible(file, showHiddenFiles) && (file.isDirectory() ||
|
||||
file.getName().equalsIgnoreCase(ManifestFileUtil.MANIFEST_FILE_NAME));
|
||||
}
|
||||
};
|
||||
descriptor.setTitle("Specify Path to MANIFEST.MF file");
|
||||
final VirtualFile[] files = FileChooser.chooseFiles(myContext.getProject(), descriptor);
|
||||
if (files.length != 1) return;
|
||||
|
||||
final String path = files[0].getPath();
|
||||
PackagingElementFactory.getInstance().addFileCopy(myElement, ManifestFileUtil.MANIFEST_DIR_NAME, path);
|
||||
myContext.getThisArtifactEditor().updateLayoutTree();
|
||||
updateComponents(ManifestFileUtil.createManifestFileConfiguration(files[0]));
|
||||
apply();
|
||||
}
|
||||
|
||||
private void updateComponents(@NotNull ManifestFileConfiguration configuration) {
|
||||
final String manifestFilePath = configuration.getManifestFilePath();
|
||||
final String card;
|
||||
if (manifestFilePath != null) {
|
||||
card = "properties";
|
||||
myManifestPathField.setText(FileUtil.toSystemDependentName(manifestFilePath));
|
||||
myMainClassField.setText(StringUtil.notNullize(configuration.getMainClass()));
|
||||
myClasspathField.setText(StringUtil.join(configuration.getClasspath(), " "));
|
||||
}
|
||||
else {
|
||||
card = "buttons";
|
||||
myManifestPathField.setText("");
|
||||
}
|
||||
((CardLayout)myPropertiesPanel.getLayout()).show(myPropertiesPanel, card);
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
myTitleLabel.setText("'" + myElement.getName() + "' manifest properties:");
|
||||
myMainClassField.setText(StringUtil.notNullize(myManifestFileConfiguration.getMainClass()));
|
||||
myClasspathField.setText(StringUtil.join(myManifestFileConfiguration.getClasspath(), " "));
|
||||
myManifestFilePathField.setText(FileUtil.toSystemDependentName(StringUtil.notNullize(myManifestFileConfiguration.getManifestFilePath())));
|
||||
createManifestFileIfNeeded();
|
||||
myManifestNotFoundLabel.setText("Manifest.mf file not found in '" + myElement.getName() + "'");
|
||||
final VirtualFile file = ManifestFileUtil.findManifestFile(myElement, myContext, myContext.getArtifactType());
|
||||
String path = file != null ? file.getPath() : null;
|
||||
if (!Comparing.equal(path, myManifestFileConfiguration.getManifestFilePath())) {
|
||||
myManifestFileConfiguration.copyFrom(ManifestFileUtil.createManifestFileConfiguration(file));
|
||||
}
|
||||
updateComponents(myManifestFileConfiguration);
|
||||
}
|
||||
|
||||
public boolean isModified() {
|
||||
@@ -128,7 +221,7 @@ public abstract class ElementWithManifestPropertiesPanel<E extends CompositeElem
|
||||
|
||||
@Nullable
|
||||
private String getConfiguredManifestPath() {
|
||||
final String path = myManifestFilePathField.getText();
|
||||
final String path = myManifestPathField.getText();
|
||||
return path.length() != 0 ? FileUtil.toSystemIndependentName(path) : null;
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -17,6 +17,7 @@ package com.intellij.packaging.artifacts;
|
||||
|
||||
import com.intellij.packaging.elements.CompositePackagingElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
@@ -38,4 +39,6 @@ public interface ModifiableArtifactModel extends ArtifactModel {
|
||||
|
||||
void commit();
|
||||
|
||||
@Nullable
|
||||
Artifact getModifiableCopy(Artifact artifact);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
* @author nik
|
||||
*/
|
||||
public interface ArtifactEditor {
|
||||
void updateLayoutTree();
|
||||
|
||||
void putLibraryIntoDefaultLocation(@NotNull Library library);
|
||||
|
||||
|
||||
@@ -43,12 +43,16 @@ public interface ArtifactEditorContext extends PackagingElementResolvingContext
|
||||
@NotNull
|
||||
ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType);
|
||||
|
||||
boolean isManifestFile(String path);
|
||||
|
||||
|
||||
CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
|
||||
|
||||
void editLayout(@NotNull Artifact artifact, Runnable runnable);
|
||||
|
||||
ArtifactEditor getOrCreateEditor(Artifact originalArtifact);
|
||||
|
||||
ArtifactEditor getThisArtifactEditor();
|
||||
|
||||
void selectArtifact(@NotNull Artifact artifact);
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package com.intellij.packaging.ui;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -26,18 +29,25 @@ public class ManifestFileConfiguration {
|
||||
private String myMainClass;
|
||||
private String myManifestFilePath;
|
||||
|
||||
public ManifestFileConfiguration(ManifestFileConfiguration configuration) {
|
||||
myClasspath = new ArrayList<String>(configuration.getClasspath());
|
||||
myMainClass = configuration.getMainClass();
|
||||
myManifestFilePath = configuration.getManifestFilePath();
|
||||
public ManifestFileConfiguration(@NotNull ManifestFileConfiguration configuration) {
|
||||
copyFrom(configuration);
|
||||
}
|
||||
|
||||
public ManifestFileConfiguration(List<String> classpath, String mainClass, String manifestFilePath) {
|
||||
myClasspath = classpath;
|
||||
public ManifestFileConfiguration(@Nullable List<String> classpath, @Nullable String mainClass, @Nullable String manifestFilePath) {
|
||||
if (classpath != null) {
|
||||
myClasspath.addAll(classpath);
|
||||
}
|
||||
myMainClass = mainClass;
|
||||
myManifestFilePath = manifestFilePath;
|
||||
}
|
||||
|
||||
public void copyFrom(@NotNull ManifestFileConfiguration configuration) {
|
||||
myClasspath.clear();
|
||||
myClasspath.addAll(configuration.getClasspath());
|
||||
myMainClass = configuration.getMainClass();
|
||||
myManifestFilePath = configuration.getManifestFilePath();
|
||||
}
|
||||
|
||||
public List<String> getClasspath() {
|
||||
return myClasspath;
|
||||
}
|
||||
|
||||
+8
@@ -66,6 +66,10 @@ public class ArtifactEditorContextImpl implements ArtifactEditorContext {
|
||||
return myParent.getManifestFile(element, artifactType);
|
||||
}
|
||||
|
||||
public boolean isManifestFile(String path) {
|
||||
return myParent.isManifestFile(path);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Project getProject() {
|
||||
return myParent.getProject();
|
||||
@@ -83,6 +87,10 @@ public class ArtifactEditorContextImpl implements ArtifactEditorContext {
|
||||
return myParent.getOrCreateEditor(artifact);
|
||||
}
|
||||
|
||||
public ArtifactEditor getThisArtifactEditor() {
|
||||
return myEditor;
|
||||
}
|
||||
|
||||
public void selectArtifact(@NotNull Artifact artifact) {
|
||||
ProjectStructureConfigurable.getInstance(getProject()).select(artifact, true);
|
||||
}
|
||||
|
||||
+6
-4
@@ -372,6 +372,10 @@ public class ArtifactEditorImpl implements ArtifactEditorEx {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLayoutTree() {
|
||||
myLayoutTreeComponent.rebuildTree();
|
||||
}
|
||||
|
||||
public void putLibraryIntoDefaultLocation(@NotNull Library library) {
|
||||
myLayoutTreeComponent.putIntoDefaultLocations(Collections.singletonList(new LibrarySourceItem(library)));
|
||||
}
|
||||
@@ -393,10 +397,8 @@ public class ArtifactEditorImpl implements ArtifactEditorEx {
|
||||
|
||||
final CompositePackagingElement<?> oldRootElement = getRootElement();
|
||||
final CompositePackagingElement<?> newRootElement = artifactType.createRootElement(getArtifact().getName());
|
||||
if (!newRootElement.getType().equals(oldRootElement.getType())) {
|
||||
ArtifactUtil.copyChildren(oldRootElement, newRootElement, myProject);
|
||||
myLayoutTreeComponent.setRootElement(newRootElement);
|
||||
}
|
||||
ArtifactUtil.copyChildren(oldRootElement, newRootElement, myProject);
|
||||
myLayoutTreeComponent.setRootElement(newRootElement);
|
||||
}
|
||||
|
||||
private class MyDataProvider implements TypeSafeDataProvider {
|
||||
|
||||
+2
@@ -34,6 +34,8 @@ public interface ArtifactsStructureConfigurableContext extends PackagingElementR
|
||||
@NotNull
|
||||
ManifestFileConfiguration getManifestFile(CompositePackagingElement<?> element, ArtifactType artifactType);
|
||||
|
||||
boolean isManifestFile(String path);
|
||||
|
||||
CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact);
|
||||
|
||||
void editLayout(@NotNull Artifact artifact, Runnable action);
|
||||
|
||||
+15
-7
@@ -77,15 +77,19 @@ class ArtifactsStructureConfigurableContextImpl implements ArtifactsStructureCon
|
||||
public CompositePackagingElement<?> getRootElement(@NotNull Artifact artifact) {
|
||||
artifact = getOriginalArtifact(artifact);
|
||||
if (myModifiableModel != null) {
|
||||
final CompositePackagingElement<?> rootElement = myModifiableModel.getArtifactByOriginal(artifact).getRootElement();
|
||||
if (rootElement != artifact.getRootElement()) {
|
||||
myModifiableRoots.put(artifact, rootElement);
|
||||
final Artifact modifiableArtifact = myModifiableModel.getModifiableCopy(artifact);
|
||||
if (modifiableArtifact != null) {
|
||||
myModifiableRoots.put(artifact, modifiableArtifact.getRootElement());
|
||||
}
|
||||
}
|
||||
CompositePackagingElement<?> root = myModifiableRoots.get(artifact);
|
||||
return getOrCreateModifiableRootElement(artifact);
|
||||
}
|
||||
|
||||
private CompositePackagingElement<?> getOrCreateModifiableRootElement(Artifact originalArtifact) {
|
||||
CompositePackagingElement<?> root = myModifiableRoots.get(originalArtifact);
|
||||
if (root == null) {
|
||||
root = ArtifactUtil.copyFromRoot(artifact.getRootElement(), myProject);
|
||||
myModifiableRoots.put(artifact, root);
|
||||
root = ArtifactUtil.copyFromRoot(originalArtifact.getRootElement(), myProject);
|
||||
myModifiableRoots.put(originalArtifact, root);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
@@ -94,7 +98,7 @@ class ArtifactsStructureConfigurableContextImpl implements ArtifactsStructureCon
|
||||
artifact = getOriginalArtifact(artifact);
|
||||
final ModifiableArtifact modifiableArtifact = getModifiableArtifactModel().getOrCreateModifiableArtifact(artifact);
|
||||
if (modifiableArtifact.getRootElement() == artifact.getRootElement()) {
|
||||
modifiableArtifact.setRootElement(getRootElement(artifact));
|
||||
modifiableArtifact.setRootElement(getOrCreateModifiableRootElement(artifact));
|
||||
}
|
||||
action.run();
|
||||
myContext.getDaemonAnalyzer().queueUpdate(new ArtifactProjectStructureElement(myContext, this, artifact));
|
||||
@@ -144,6 +148,10 @@ class ArtifactsStructureConfigurableContextImpl implements ArtifactsStructureCon
|
||||
return myManifestFilesInfo.getManifestFile(element, artifactType, this);
|
||||
}
|
||||
|
||||
public boolean isManifestFile(String path) {
|
||||
return myManifestFilesInfo.isManifestFile(path);
|
||||
}
|
||||
|
||||
public ManifestFilesInfo getManifestFilesInfo() {
|
||||
return myManifestFilesInfo;
|
||||
}
|
||||
|
||||
+2
-1
@@ -130,7 +130,7 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
|
||||
|
||||
public void updatePropertiesPanel(final boolean force) {
|
||||
final PackagingElement<?> selected = getSelection().getElementIfSingle();
|
||||
if (force || Comparing.equal(selected, mySelectedElementInfo.myElement)) {
|
||||
if (!force && Comparing.equal(selected, mySelectedElementInfo.myElement)) {
|
||||
return;
|
||||
}
|
||||
mySelectedElementInfo.save();
|
||||
@@ -504,6 +504,7 @@ public class LayoutTreeComponent implements DnDTarget, Disposable {
|
||||
else {
|
||||
cardLayout.show(myPropertiesPanelWrapper, EMPTY_CARD);
|
||||
}
|
||||
myPropertiesPanelWrapper.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -24,6 +24,7 @@ import com.intellij.packaging.elements.CompositePackagingElement;
|
||||
import com.intellij.packaging.elements.PackagingElementResolvingContext;
|
||||
import com.intellij.packaging.impl.elements.ManifestFileUtil;
|
||||
import com.intellij.packaging.ui.ManifestFileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
@@ -79,4 +80,13 @@ public class ManifestFilesInfo {
|
||||
myManifestFiles.clear();
|
||||
myOriginalManifestFiles.clear();
|
||||
}
|
||||
|
||||
public boolean isManifestFile(@NotNull String path) {
|
||||
for (ManifestFileConfiguration configuration : myManifestFiles.values()) {
|
||||
if (path.equals(configuration.getManifestFilePath())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user