Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.mftf.TestNameIndex" />
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.js.RequireJsIndex" />
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.js.MagentoLibJsIndex" />
<fileBasedIndex implementation="com.magento.idea.magento2plugin.stubs.indexes.xml.AclResourceIndex" />

<codeInsight.lineMarkerProvider language="PHP" implementationClass="com.magento.idea.magento2plugin.linemarker.php.PluginLineMarkerProvider"/>
<codeInsight.lineMarkerProvider language="PHP" implementationClass="com.magento.idea.magento2plugin.linemarker.php.PluginTargetLineMarkerProvider"/>
Expand Down Expand Up @@ -169,6 +170,13 @@
enabledByDefault="true" level="ERROR"
implementationClass="com.magento.idea.magento2plugin.inspections.xml.ModuleDeclarationInModuleXmlInspection"/>

<localInspection language="XML" groupPath="XML"
shortName="AclResourceXmlInspection"
displayName="Inspection for the Title XML required attribute in the `etc/acl.xml` file"
groupName="Magento 2"
enabledByDefault="true" level="ERROR"
implementationClass="com.magento.idea.magento2plugin.inspections.xml.AclResourceXmlInspection"/>

<libraryRoot id=".phpstorm.meta.php" path=".phpstorm.meta.php/" runtime="false"/>

<internalFileTemplate name="Magento Module Composer"/>
Expand Down
2 changes: 2 additions & 0 deletions resources/magento2/inspection.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ inspection.observer.duplicateInOtherPlaces=The observer name "{0}" for event "{1
inspection.cache.disabledCache=Cacheable false attribute on the default layout will disable cache site-wide
inspection.moduleDeclaration.warning.wrongModuleName=Provided module name "{0}" does not match expected "{1}"
inspection.moduleDeclaration.fix=Fix module name
inspection.aclResource.error.missingAttribute=Attribute "{0}" is required
inspection.aclResource.error.idAttributeCanNotBeEmpty=Attribute value "{0}" can not be empty
36 changes: 28 additions & 8 deletions src/com/magento/idea/magento2plugin/indexes/IndexManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,39 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.indexes;

import com.intellij.util.indexing.FileBasedIndexImpl;
import com.intellij.util.indexing.ID;
import com.magento.idea.magento2plugin.stubs.indexes.*;
import com.magento.idea.magento2plugin.stubs.indexes.BlockNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.ContainerNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.EventNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.EventObserverIndex;
import com.magento.idea.magento2plugin.stubs.indexes.ModuleNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.ModulePackageIndex;
import com.magento.idea.magento2plugin.stubs.indexes.PluginIndex;
import com.magento.idea.magento2plugin.stubs.indexes.VirtualTypeIndex;
import com.magento.idea.magento2plugin.stubs.indexes.WebApiTypeIndex;
import com.magento.idea.magento2plugin.stubs.indexes.graphql.GraphQlResolverIndex;
import com.magento.idea.magento2plugin.stubs.indexes.js.MagentoLibJsIndex;
import com.magento.idea.magento2plugin.stubs.indexes.js.RequireJsIndex;
import com.magento.idea.magento2plugin.stubs.indexes.graphql.GraphQlResolverIndex;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.*;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.ActionGroupIndex;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.DataIndex;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.PageIndex;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.SectionIndex;
import com.magento.idea.magento2plugin.stubs.indexes.mftf.TestNameIndex;
import com.magento.idea.magento2plugin.stubs.indexes.xml.AclResourceIndex;
import com.magento.idea.magento2plugin.stubs.indexes.xml.PhpClassNameIndex;

@SuppressWarnings({"PMD.ClassNamingConventions", "PMD.UseUtilityClass"})
public class IndexManager {

/**
* Refresh Magento 2 indexes.
*/
public static void manualReindex() {
ID<?, ?>[] indexIds = new ID<?, ?>[] {
final ID<?, ?>[] indexIds = new ID<?, ?>[] {//NOPMD
// php
ModulePackageIndex.KEY,
// xml|di configuration
Expand All @@ -32,6 +50,8 @@ public static void manualReindex() {
WebApiTypeIndex.KEY,
ModuleNameIndex.KEY,
PhpClassNameIndex.KEY,
//acl
AclResourceIndex.KEY,
//require_js
RequireJsIndex.KEY,
MagentoLibJsIndex.KEY,
Expand All @@ -41,15 +61,15 @@ public static void manualReindex() {
PageIndex.KEY,
SectionIndex.KEY,
TestNameIndex.KEY,
//graphql
//graphql
GraphQlResolverIndex.KEY
};

for (ID<?, ?> id: indexIds) {
for (final ID<?, ?> id: indexIds) {
try {
FileBasedIndexImpl.getInstance().requestRebuild(id);
FileBasedIndexImpl.getInstance().scheduleRebuild(id, new Throwable());
} catch (NullPointerException exception) {
FileBasedIndexImpl.getInstance().scheduleRebuild(id, new Throwable());//NOPMD
} catch (NullPointerException exception) { //NOPMD
//that's fine, indexer is not present in map java.util.Map.get
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.inspections.xml;

import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.XmlSuppressableInspectionTool;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.XmlElementVisitor;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.indexing.FileBasedIndex;
import com.magento.idea.magento2plugin.bundles.InspectionBundle;
import com.magento.idea.magento2plugin.magento.files.ModuleAclXml;
import com.magento.idea.magento2plugin.stubs.indexes.xml.AclResourceIndex;
import java.util.List;
import org.jetbrains.annotations.NotNull;

public class AclResourceXmlInspection extends XmlSuppressableInspectionTool {

@NotNull
@Override
public PsiElementVisitor buildVisitor(
final @NotNull ProblemsHolder problemsHolder,
final boolean isOnTheFly
) {
return new XmlElementVisitor() {
private final InspectionBundle inspectionBundle = new InspectionBundle();

@Override
public void visitXmlTag(final XmlTag xmlTag) {
final PsiFile file = xmlTag.getContainingFile();
final String filename = file.getName();
if (!filename.equals(ModuleAclXml.FILE_NAME)) {
return;
}

if (!xmlTag.getName().equals(ModuleAclXml.XML_TAG_RESOURCE)) {
return;
}

final XmlAttribute identifier = xmlTag.getAttribute(ModuleAclXml.XML_ATTR_ID);
if (identifier == null) {
//should be handled by schema
return;
}

final String idValue = identifier.getValue();
if (idValue == null || idValue.isEmpty()) {
problemsHolder.registerProblem(
identifier,
inspectionBundle.message(
"inspection.aclResource.error.idAttributeCanNotBeEmpty",
"id"
),
ProblemHighlightType.WARNING
);
return;
}

final XmlAttribute title = xmlTag.getAttribute(ModuleAclXml.XML_ATTR_TITLE);
if (title != null && title.getValue() != null) {
return;
}

final List<String> titles =
FileBasedIndex.getInstance().getValues(
AclResourceIndex.KEY,
idValue,
GlobalSearchScope.allScope(file.getProject()
)
);

if (titles.isEmpty()) {
problemsHolder.registerProblem(
identifier,
inspectionBundle.message(
"inspection.aclResource.error.missingAttribute",
"title"
),
ProblemHighlightType.WARNING
);
}
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.magento.files;

@SuppressWarnings({"PMD.ClassNamingConventions", "PMD.FieldNamingConventions"})
public class ModuleAclXml {
public static String XML_ATTR_ID = "id";
public static String XML_ATTR_TITLE = "title";
public static String XML_TAG_RESOURCE = "resource";
public static String XML_TAG_RESOURCES = "resources";
public static String XML_TAG_ACL = "acl";
public static String FILE_NAME = "acl.xml";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

package com.magento.idea.magento2plugin.stubs.indexes.xml;

import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndexExtension;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.ID;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.xml.impl.DomApplicationComponent;
import com.magento.idea.magento2plugin.magento.files.ModuleAclXml;
import com.magento.idea.magento2plugin.project.Settings;
import gnu.trove.THashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;

public class AclResourceIndex extends FileBasedIndexExtension<String, String> {
public static final ID<String, String> KEY = ID.create(
"com.magento.idea.magento2plugin.stubs.indexes.acl_resources");
private final KeyDescriptor<String> myKeyDescriptor = new EnumeratorStringDescriptor();

@NotNull
@Override
public DataIndexer<String, String, FileContent> getIndexer() {
return inputData -> {
final Map<String, String> map = new THashMap<>();//NOPMD
final PsiFile psiFile = inputData.getPsiFile();
if (!Settings.isEnabled(psiFile.getProject())) {
return map;
}

if (psiFile instanceof XmlFile) {
final XmlDocument xmlDocument = ((XmlFile) psiFile).getDocument();
if (xmlDocument != null) {
final XmlTag xmlRootTag = xmlDocument.getRootTag();
if (xmlRootTag != null) { //NOPMD
parseRootTag(map, xmlRootTag);
}
}
}
return map;
};
}

protected void parseRootTag(final Map<String, String> map, final XmlTag xmlRootTag) {
for (final XmlTag aclTag : xmlRootTag.findSubTags(ModuleAclXml.XML_TAG_ACL)) {
for (final XmlTag resourcesTag : aclTag.findSubTags(ModuleAclXml.XML_TAG_RESOURCES)) {
parseResourceTag(map, resourcesTag);
}
}
}

private void parseResourceTag(final Map<String, String> map, final XmlTag resourcesTag) {
for (final XmlTag resourceTag : resourcesTag.findSubTags(ModuleAclXml.XML_TAG_RESOURCE)) {
final String identifier = resourceTag.getAttributeValue(ModuleAclXml.XML_ATTR_ID);
final String title = resourceTag.getAttributeValue(ModuleAclXml.XML_ATTR_TITLE);

if (identifier != null && title != null && !identifier.isEmpty()
&& !title.isEmpty()) {
map.put(identifier, title);
}

parseResourceTag(map, resourceTag);
}
}

@NotNull
@Override
public ID<String, String> getName() {
return KEY;
}

@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return this.myKeyDescriptor;
}

@NotNull
@Override
public DataExternalizer<String> getValueExternalizer() {
return EnumeratorStringDescriptor.INSTANCE;
}

@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return file ->
file.getFileType() == XmlFileType.INSTANCE
&& file.getName().equalsIgnoreCase(ModuleAclXml.FILE_NAME);
}

@Override
public boolean dependsOnFileContent() {
return true;
}

@Override
public int getVersion() {
return DomApplicationComponent.getInstance().getCumulativeVersion(false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="" />
</resources>
</acl>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Unique_Acl::resource" />
</resources>
</acl>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<!-- Override existing resource in the project -->
<resource id="Magento_Catalog::test" />
</resources>
</acl>
</config>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Catalog::test" title="Test Resource"/>
</resources>
</acl>
</config>
Loading