• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Script I made: Iterate Action Over Layers

Participant ,
Jul 04, 2016 Jul 04, 2016

Copy link to clipboard

Copied

Hey, just cobbled together this script that I'm already finding useful in my own work, so I figured I'd share, and maybe see if anyone has suggestions for improvement.

What it does, is apply a selected action to each of the selected layers individually.

So for example, if you had 50 layers that you wanted to each rotate by 90 degrees in place, you'd record a rotate-by-90-degrees action, select all the layers you want to apply it to and run the script with the action selected.

#target photoshop

var scriptName = "NinjaScript IterateActionLayers";

var scriptVersion = "0002";

function cID (inVal) { return charIDToTypeID(inVal);}

function sID (inVal) { return stringIDToTypeID(inVal);}

var currentActionSets = getActionSets();

main();

function main()

{

    app.bringToFront();

    optionsDialog();

}

function optionsDialog()

{

    var ButtonWidth = 100;

   

  OpenOptionsDialog = new Window("dialog", scriptName + " v" + scriptVersion);

  OpenOptionsDialog.orientation = 'column';

  OpenOptionsDialog.alignChildren = 'left';

    mainGroup = OpenOptionsDialog.add("group");

    mainGroup.orientation = 'column';

    mainGroup.alignChildren = 'left';

    mainGroup.alignment = 'left';    

    var actionSetGroup = mainGroup.add("group");

    actionSetGroup.orientation = 'row';

    actionSetGroup.add("statictext",undefined, "ActionSet: ")

    var DDActionSet = actionSetGroup.add("dropdownlist",undefined, "")

    DDActionSet.preferredSize.width = 150;

      

    for (var i = 0; i < currentActionSets.length; i++)

    {

            DDActionSet.add("item", currentActionSets);

    }

    DDActionSet.selection = 0;

      

      

    var actionGroup = mainGroup.add("group");

    actionGroup.orientation = 'row';

    actionGroup.add("statictext",undefined, "Action:      ")

    DDActions = actionGroup.add("dropdownlist",undefined, "")

    DDActions.preferredSize.width = 150;

   

    function populateDDActions (inSet)

    {

        DDActions.removeAll();

       

        for (var i = 0; i < currentActionSets[inSet].actions.length; i++)

        {

            DDActions.add("item", currentActionSets[inSet].actions);

        }

        DDActions.selection = 0;

    }

    DDActionSet.onChange = function()

    {

        populateDDActions(DDActionSet.selection.index);

    }

    DDActionSet.onChange();

   

    mainGroup.add("statictext", undefined, "");

    

    ButtonGroup = mainGroup.add("group");

    ButtonGroup.orientation = 'row';

    ButtonGroup.alignChildren = 'center';

    ButtonGroup.alignment = 'top';    

           

    buttonRun= ButtonGroup.add("button",undefined, "Run")

    buttonRun.preferredSize.width = ButtonWidth;

    buttonRun.onClick = function()

   {

       var SelectedLayers = getSelectedLayers();

      

        for (var i = 0; i < SelectedLayers.length; i++)

        {

            activeDocument.activeLayer = SelectedLayers;

            doAction(DDActions.selection.text, DDActionSet.selection.text);

           

        }

        OpenOptionsDialog.close()

    }

   

    buttonClose= ButtonGroup.add("button",undefined, "Exit")

    buttonClose.preferredSize.width = ButtonWidth;

    buttonClose.onClick = function() {OpenOptionsDialog.close()}      

    //Show window

  OpenOptionsDialog.center();

  var result = OpenOptionsDialog.show();

}

function getActionSets()

  var i = 1; 

  var sets = []; 

  while (true) { 

    var ref = new ActionReference(); 

    ref.putIndex(cID("ASet"), i); 

    var desc; 

    var lvl = $.level; 

    $.level = 0; 

    try { 

      desc = executeActionGet(ref); 

    } catch (e) { 

      break;    // all done 

    } finally { 

      $.level = lvl; 

    } 

    if (desc.hasKey(cID("Nm  "))) { 

      var set = {}; 

      set.index = i; 

      set.name = desc.getString(cID("Nm  ")); 

      set.toString = function() { return this.name; }; 

      set.count = desc.getInteger(cID("NmbC")); 

      set.actions = []; 

      for (var j = 1; j <= set.count; j++) { 

        var ref = new ActionReference(); 

        ref.putIndex(cID('Actn'), j); 

        ref.putIndex(cID('ASet'), set.index); 

        var adesc = executeActionGet(ref); 

        var actName = adesc.getString(cID('Nm  ')); 

        set.actions.push(actName); 

      } 

      sets.push(set); 

    } 

    i++; 

  } 

  return sets; 

}; 

function getSelectedLayers()

{

  var resultLayers=new Array();

  try{

    var descGrp = new ActionDescriptor();

    var refGrp = new ActionReference();

    refGrp.putEnumerated(cID( "Lyr " ),cID( "Ordn" ),cID( "Trgt" ));

    descGrp.putReference(cID( "null" ), refGrp );

    executeAction( sID( "groupLayersEvent" ), descGrp, DialogModes.NO );

    for (var ix=0;ix<app.activeDocument.activeLayer.layers.length;ix++){resultLayers.push(app.activeDocument.activeLayer.layers[ix])}

    var desc5 = new ActionDescriptor();

    var ref2 = new ActionReference();

    ref2.putEnumerated( cID( "HstS" ), cID( "Ordn" ), cID( "Prvs" ) );

    desc5.putReference( cID( "null" ), ref2 );

    executeAction( cID( "slct" ), desc5, DialogModes.NO );

  } catch (err) { }

  return resultLayers;

}  

TOPICS
Actions and scripting

Views

7.9K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Contributor ,
Jul 06, 2016 Jul 06, 2016

Copy link to clipboard

Copied

I like your sharing

but I prefer that after starting the action the panel remains open

so I can apply other effects.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Nov 15, 2017 Nov 15, 2017

Copy link to clipboard

Copied

@tssee: just comment out line 97 to prevent the panel from closing on Run button click.

Davide Barranca - PS developer and author
www.ps-scripting.com

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jul 22, 2016 Jul 22, 2016

Copy link to clipboard

Copied

Hi EnsilZah_,

Thanks a lot for posting this script. I am looking to loop an action and I used the code you provided. It almost worked for what I need, but the action did not analyze each layer individually, rather took the first reading (Color Range) from the first layer and applied it to all of the following layers below it. If I "play" the action on each layer individually it works fine. I already have "select next sequential layer" at the end of the action, so I just need my action to loop so that each layer is individually analyzed by the action. Do you know how I can repeat / loop an action?

Thanks!

Jackson

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 22, 2016 Jul 22, 2016

Copy link to clipboard

Copied

I Recorded an action to load the current layer transparence as a selection then fill that selection with a random color. I was hoping that the script would make each layer the active layer before playing the action.  I ran the script and only the background layer which was the current layer was filled.  I took a quick look at the script and saw the script would only play the action on  selected Layers. So I select all the layers and each layer retain its transparency and the non transparent pixels were filled.  So each layer selection worked.

It may be your action at fault.  You wrote that you used Color range and the first layers color range  selection  was used on all layers. The would happen If your action did a color range and saved that selection did other stuff than loaded the saved selection and used it.

After using the save selection the action needs to delete the saved selection alpha channel.  For the next time the Action is played It would save the new color range selection and save it This would not replace the first save it would be an additional  alpha channel with the same name or different name.   The Action is recorded to either load the first alpha channel or the alpha channel with the name you used. Which would load the first one with that name.  If you had more than 53 selected layers selected the Action would  also fail with an error Photoshop only  supports up to 53 alpha channels.

The script also has problems.  If a select the background layer and layers in a layer group for example the script does not work.

JJMack

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jul 22, 2016 Jul 22, 2016

Copy link to clipboard

Copied

Thanks JJMack,

I tried to recreate the action as you mentioned, and make sure to deselect the color range selection at the end of the action. The issue continued to persist, but I noticed that as the script was running, it kept all of the layers visible as it went down the layers (including the top layer, which it was sampling). I added a "hide layer" command at the end of the action, so now the "Color Range" can see the current layer to sample it. It then works.

Thanks so much for your help!

Jackson

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 22, 2016 Jul 22, 2016

Copy link to clipboard

Copied

If your action save the selection and later  use the save selection you need to DELETE the saved selection after loading and using it, Not just deselect.

I do not create script that let uses add there action to the process.  Action may do things that change the document in some way that will cause the script to fail.  For example delete the current layer add some number of layers changes the to document like that may mess up what the script is doing.

If you have selected five layer and a script like this one note the layers to be processed like create as list  layer Name and the layer index number. Then proceeds to make a layer in the list  the active layer a plays the user action.   If the action changes the layer stack will the scripts layer list still be valid? Layer name may mot be unique.

There are a couple of well known scripts the allow user to add Action into the Script process. The Image Processor scripts the let users add actions.  However, the action is player after the Image Processors processed a dupe of the current Image being process for outputting a image file.  The document is ready to be  saveas a a file.  All the script does after the playing and the action completes is save the active document  and close it or resize,  save and  close the active document. The image processor then move on the processing the next output file by duping the current source image or then next soource imat or the processing is done.

I may write a script like this one for my own use. However  I would not make it public for actions could easily mess with the layer stack.

JJMack

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jul 22, 2016 Jul 22, 2016

Copy link to clipboard

Copied

In the other thread you wrote you wanted to play you action on all layers,  The Script posted here is not designed to do that.  It is hard for me to believe an action could be recorderdto work on all layers on any layered  document.  There are too many types of layers action can not use logic to find out the kind of layer the active layer is.  An action could be written for a specific document the action is recorded for..

It would be very easy for an action to mess Up a simple script that was playing an action on each layer by looping through the layer changing the active layer and playing.

A script like that is very simple to write pulling off the action may not be simple. I'll post the script.  You will need the change the first two lines to set the ActionName and the ActionSet name to your action set and action.  I will not make it a dialog.  IMO the script is not generally useful.  Us at you own risk....

var ActionName = "RandonFillColor";

var ActionSet = "temp";

if (!documents.length) { alert('There are no documents open.', 'No Document');}

else {processArtLayers(activeDocument);}

function processArtLayers(obj) {

    for( var i = obj.artLayers.length-1; 0 <= i; i--) { activeDocument.activeLayer = obj.artLayers; doAction(ActionName, ActionSet);}

    for( var i = obj.layerSets.length-1; 0 <= i; i--) {processArtLayers(obj.layerSets); } // Process Layer Set Layers

}

JJMack

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jul 23, 2016 Jul 23, 2016

Copy link to clipboard

Copied

Hi JJMack,

Thank you for your help and expertise. I see what you're saying that you can't simply loop an action, because faults may come into play.

Fortunately, I was able to resolve the "Color Range" issue, by adding a "Hide Current Layer" at the end of the action. Then when the action samples each layer, it hides the top layer revealing the lower one, so that the correct sample is taken (instead of just the top layer).

I will see if I can use your script as another way.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Nov 14, 2017 Nov 14, 2017

Copy link to clipboard

Copied

Hello!

My apologies for bumping this back up. But does anybody know how I'd edit this script to just run a pre-chosen action, rather than giving the dialog box to choose? Or would I be better off writing something from scratch? Basically, I don't know how to make a for loop run only on selected layers.

I want to integrate this into a much broader set of scripts/actions that runs start to finish without the need for any human input, but my scripting knowledge is still pretty rudimentary. Specifically, I want to delete the layer masks of all selected layers, and replace them with a layer mask of the current marquee selection. So maybe it doesn't even need to run an action, but rather the 'replace mask' instruction is just built into the script.

If anyone can help, I'd appreciate this so much!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Apr 13, 2020 Apr 13, 2020

Copy link to clipboard

Copied

I am trying to make this script work with Photoshop CC 2020 in OSX Catalina. When I run it, the dialog displays, the dropDown lists display the actions set and action in an erratic mode. The display order of the items is not working correctly. What can be the source of the erratic display? 

 

Screen Shot 2020-04-13 at 6.30.06 PM.pngScreen Shot 2020-04-13 at 6.30.17 PM.png

 

 

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Apr 13, 2020 Apr 13, 2020

Copy link to clipboard

Copied

DDActions.add("item", currentActionSets[inSet].actions);

replace with:

DDActions.add("item", currentActionSets[inSet].actions[i]);

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Apr 13, 2020 Apr 13, 2020

Copy link to clipboard

Copied

Dmitry, thank you for your awesome help! I added [i] to lines 63 and 97 to fix the loops. Now the dropdown lists display correctly. After, the script still did not execute the actions correctly. I commented out the getSeelctedFiles() function and edited out the loop for (var i = 0; i < SelectedLayers.length; i++). You can see the changes in the code. The script works well now, the dropdown lists display correctly and the actions run without errors. I am wondering if there is a way to preset the dropdown list to a specific action set and action within the set. Instead of showing the topmost action-set and action in the dropdown list.

 

#target photoshop

var scriptName = "NinjaScript IterateActionLayers";

var scriptVersion = "0002";

function cID (inVal) { return charIDToTypeID(inVal);}

function sID (inVal) { return stringIDToTypeID(inVal);}

var currentActionSets = getActionSets();

main();

function main()

{

app.bringToFront();

optionsDialog();

}

function optionsDialog()

{

var ButtonWidth = 100;

 

OpenOptionsDialog = new Window("dialog", scriptName + " v" + scriptVersion);

OpenOptionsDialog.orientation = 'column';

OpenOptionsDialog.alignChildren = 'left';

mainGroup = OpenOptionsDialog.add("group");

mainGroup.orientation = 'column';

mainGroup.alignChildren = 'left';

mainGroup.alignment = 'left';

var actionSetGroup = mainGroup.add("group");

actionSetGroup.orientation = 'row';

actionSetGroup.add("statictext",undefined, "ActionSet: ")

var DDActionSet = actionSetGroup.add("dropdownlist",undefined, "")

DDActionSet.preferredSize.width = 150;

 

for (var i = 0; i < currentActionSets.length; i++)

{

DDActionSet.add("item", currentActionSets[i]); // edit 041320

}

DDActionSet.selection = 0;

 

 

var actionGroup = mainGroup.add("group");

actionGroup.orientation = 'row';

actionGroup.add("statictext",undefined, "Action: ")

DDActions = actionGroup.add("dropdownlist",undefined, "")

DDActions.preferredSize.width = 150;

 

function populateDDActions (inSet)

{

DDActions.removeAll();

 

for (var i = 0; i < currentActionSets[inSet].actions.length; i++)

{

DDActions.add("item", currentActionSets[inSet].actions[i]); //edit 041320

}

DDActions.selection = 0;

}

DDActionSet.onChange = function()

{

populateDDActions(DDActionSet.selection.index);

}

DDActionSet.onChange();

 

mainGroup.add("statictext", undefined, "");

 

ButtonGroup = mainGroup.add("group");

ButtonGroup.orientation = 'row';

ButtonGroup.alignChildren = 'center';

ButtonGroup.alignment = 'top';

 

buttonRun= ButtonGroup.add("button",undefined, "Run")

buttonRun.preferredSize.width = ButtonWidth;

buttonRun.onClick = function()

{

doAction(DDActions.selection.text, DDActionSet.selection.text);

// var SelectedLayers = getSelectedLayers();

 

// for (var i = 0; i < SelectedLayers.length; i++)

// {

// activeDocument.activeLayer = SelectedLayers;

// doAction(DDActions.selection.text, DDActionSet.selection.text);

 

// }

OpenOptionsDialog.close()

}

 

buttonClose= ButtonGroup.add("button",undefined, "Exit")

buttonClose.preferredSize.width = ButtonWidth;

buttonClose.onClick = function() {OpenOptionsDialog.close()}

//Show window

OpenOptionsDialog.center();

var result = OpenOptionsDialog.show();

}

function getActionSets()

{

var i = 1;

var sets = [];

while (true) {

var ref = new ActionReference();

ref.putIndex(cID("ASet"), i);

var desc;

var lvl = $.level;

$.level = 0;

try {

desc = executeActionGet(ref);

} catch (e) {

break; // all done

} finally {

$.level = lvl;

}

if (desc.hasKey(cID("Nm "))) {

var set = {};

set.index = i;

set.name = desc.getString(cID("Nm "));

set.toString = function() { return this.name; };

set.count = desc.getInteger(cID("NmbC"));

set.actions = [];

for (var j = 1; j <= set.count; j++) {

var ref = new ActionReference();

ref.putIndex(cID('Actn'), j);

ref.putIndex(cID('ASet'), set.index);

var adesc = executeActionGet(ref);

var actName = adesc.getString(cID('Nm '));

set.actions.push(actName);

}

sets.push(set);

}

i++;

}

return sets;

};

// function getSelectedLayers()

// {

// var resultLayers=new Array();

// try{

// var descGrp = new ActionDescriptor();

// var refGrp = new ActionReference();

// refGrp.putEnumerated(cID( "Lyr " ),cID( "Ordn" ),cID( "Trgt" ));

// descGrp.putReference(cID( "null" ), refGrp );

// executeAction( sID( "groupLayersEvent" ), descGrp, DialogModes.NO );

// for (var ix=0;ix<app.activeDocument.activeLayer.layers.length;ix++){resultLayers.push(app.activeDocument.activeLayer.layers[ix])}

// var desc5 = new ActionDescriptor();

// var ref2 = new ActionReference();

// ref2.putEnumerated( cID( "HstS" ), cID( "Ordn" ), cID( "Prvs" ) );

// desc5.putReference( cID( "null" ), ref2 );

// executeAction( cID( "slct" ), desc5, DialogModes.NO );

// } catch (err) { }

// return resultLayers;

// }

  

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Apr 13, 2020 Apr 13, 2020

Copy link to clipboard

Copied

you can select any item from list by its index

DDActionSet.selection = 0;

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Apr 14, 2020 Apr 14, 2020

Copy link to clipboard

Copied

Yes, that definitely works. I am thinking about an issue down the road. The number of Action Sets in the Action Pallet changes over time. This will influence the position and index number for a specific Action Set in DD list.  Can the index number match a specific string and prioritize the string?  The idea is that the DD list always displays the same Action Set regardless of the number of Action Sets in the DD list.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Apr 14, 2020 Apr 14, 2020

Copy link to clipboard

Copied

DDActionSet.selection = DDActionSet.find("my action set name");

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Apr 15, 2020 Apr 15, 2020

Copy link to clipboard

Copied

Thank you, Dmitry, for answering my questions. Stay awesome!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
May 12, 2020 May 12, 2020

Copy link to clipboard

Copied

Doesn't work for me. I get this error:

 

Error 1220: Illegal Argument
Line: 7
-> function cID (inVal) {return charIdToTypeID(inVal);}

 

Using photoshop cc 2020

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jul 06, 2020 Jul 06, 2020

Copy link to clipboard

Copied

Same error for me

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 08, 2020 Jul 08, 2020

Copy link to clipboard

Copied

Show your code.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Apr 14, 2020 Apr 14, 2020

Copy link to clipboard

Copied

You need to edit 2 strings

this
DDActionSet.add ("item", currentActionSets);

with this
  DDActionSet.add ("item", currentActionSets [i]);

this
DDActions.add ("item", currentActionSets [inSet] .actions);

with this
DDActions.add ("item", currentActionSets [inSet] .actions [i]);

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Sep 24, 2020 Sep 24, 2020

Copy link to clipboard

Copied

Hi EnsilZah_,

Thank you for this script!

Fixed it to work in Ps2020 and added feature to choose what layer it will target (selected, visible or all) and bunch of layer type checks, so it won't spam errors:

SomeRandomStuff

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Apr 30, 2021 Apr 30, 2021

Copy link to clipboard

Copied

Thanks so much for this! Super helpful for my work. I'm having one issue with your code though.. it's only running the script/action on a few layers before stopping (incompleted). Thoughts? I would really love to be able to use this to its full potential. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Jul 06, 2021 Jul 06, 2021

Copy link to clipboard

Copied

Sorry for not answering, didn't get any nottification about reply...

That's wierd, i was trying to combine all types of layers/groups/etc when i tested it... Can you send example for me to test and try to fix it?

Also it would be easier to contact somewhere else, meaybe email or discord, i'll DM you my here.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jul 05, 2021 Jul 05, 2021

Copy link to clipboard

Copied

Very nice! Is it possible to do this without OptionsDialog?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines