mirror of
https://github.com/openvk/openvk
synced 2024-12-23 00:51:03 +03:00
Merge branch 'master' into textarea-refactor
This commit is contained in:
commit
9f699336f7
175 changed files with 5882 additions and 1523 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,7 +5,7 @@ update.pid.old
|
||||||
Web/static/js/node_modules
|
Web/static/js/node_modules
|
||||||
|
|
||||||
tmp/*
|
tmp/*
|
||||||
!tmp/.gitkeep
|
!tmp/api-storage
|
||||||
!tmp/themepack_artifacts/.gitkeep
|
!tmp/themepack_artifacts/.gitkeep
|
||||||
themepacks/*
|
themepacks/*
|
||||||
!themepacks/.gitkeep
|
!themepacks/.gitkeep
|
||||||
|
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
46
.idea/intellij-latte/xmlSources/Latte.dtd
Normal file
46
.idea/intellij-latte/xmlSources/Latte.dtd
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!ELEMENT latte (tags,filters,variables,functions)>
|
||||||
|
<!ATTLIST latte vendor #REQUIRED>
|
||||||
|
<!ATTLIST latte version #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT tags (tag)+>
|
||||||
|
|
||||||
|
<!ELEMENT tag (arguments)?>
|
||||||
|
<!ATTLIST tag name CDATA #REQUIRED>
|
||||||
|
<!ATTLIST tag type (PAIR|UNPAIRED|UNPAIRED_ATTR|ATTR_ONLY|AUTO_EMPTY) #REQUIRED>
|
||||||
|
<!ATTLIST tag allowedFilters (true|false) #IMPLIED>
|
||||||
|
<!ATTLIST tag arguments CDATA #IMPLIED>
|
||||||
|
<!ATTLIST tag deprecatedMessage CDATA #IMPLIED>
|
||||||
|
<!ATTLIST tag multiLine (true|false) #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT arguments (argument)+>
|
||||||
|
|
||||||
|
<!ELEMENT argument EMPTY>
|
||||||
|
<!ATTLIST argument name #REQUIRED>
|
||||||
|
<!ATTLIST argument types CDATA #REQUIRED>
|
||||||
|
<!ATTLIST argument repeatable (true|false) #IMPLIED>
|
||||||
|
<!ATTLIST argument required (true|false) #IMPLIED>
|
||||||
|
<!ATTLIST argument validType #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT filters (filter)+>
|
||||||
|
|
||||||
|
<!ELEMENT filter EMPTY>
|
||||||
|
<!ATTLIST filter name #REQUIRED>
|
||||||
|
<!ATTLIST filter description CDATA #IMPLIED>
|
||||||
|
<!ATTLIST filter arguments CDATA #IMPLIED>
|
||||||
|
<!ATTLIST filter insertColons #IMPLIED>
|
||||||
|
|
||||||
|
<!ELEMENT variables (variable)+>
|
||||||
|
|
||||||
|
<!ELEMENT variable EMPTY>
|
||||||
|
<!ATTLIST variable name #REQUIRED>
|
||||||
|
<!ATTLIST variable type CDATA #REQUIRED>
|
||||||
|
|
||||||
|
<!ELEMENT functions (function)+>
|
||||||
|
|
||||||
|
<!ELEMENT function EMPTY>
|
||||||
|
<!ATTLIST function name #REQUIRED>
|
||||||
|
<!ATTLIST function arguments CDATA #REQUIRED>
|
||||||
|
<!ATTLIST function returnType #REQUIRED>
|
||||||
|
<!ATTLIST function description CDATA #IMPLIED>
|
290
.idea/intellij-latte/xmlSources/Latte.xml
Normal file
290
.idea/intellij-latte/xmlSources/Latte.xml
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin XML V0.0.1//EN" "Latte.dtd">
|
||||||
|
<latte vendor="latte" version="1">
|
||||||
|
<tags>
|
||||||
|
<tag name="_" type="AUTO_EMPTY" allowedFilters="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="=" type="UNPAIRED" allowedFilters="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="block" type="AUTO_EMPTY" allowedFilters="true" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="breakIf" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="capture" type="PAIR" allowedFilters="true" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="variable" types="VARIABLE_DEFINITION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="case" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" required="true" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="catch" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="contentType" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="content-type" types="CONTENT_TYPE" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="continueIf" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="debugbreak" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="default" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="variable" types="VARIABLE_DEFINITION_EXPRESSION" required="true" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="define" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" required="true" />
|
||||||
|
<argument name="variable" types="VARIABLE_DEFINITION_ITEM" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="do" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="dump" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="else" type="UNPAIRED_ATTR" />
|
||||||
|
<tag name="elseif" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="elseifset" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="var" types="VARIABLE,BLOCK" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="extends" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION,NONE" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="first" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="for" type="PAIR" arguments="initialization; condition; afterthought" multiLine="true" />
|
||||||
|
<tag name="foreach" type="PAIR" arguments="expression as [$key =>] $value" allowedFilters="true" multiLine="true" />
|
||||||
|
<tag name="if" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="ifset" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="var" types="VARIABLE,BLOCK,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="import" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="include" type="UNPAIRED" allowedFilters="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="BLOCK,IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="KEY_VALUE" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="includeblock" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="l" type="UNPAIRED" />
|
||||||
|
<tag name="last" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="layout" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION,NONE" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="class" type="ATTR_ONLY" arguments="class" />
|
||||||
|
<tag name="attr" type="ATTR_ONLY" arguments="attr" />
|
||||||
|
<tag name="ifcontent" type="ATTR_ONLY" />
|
||||||
|
<tag name="php" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="r" type="UNPAIRED" />
|
||||||
|
<tag name="sandbox" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="BLOCK,PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="key-value" types="KEY_VALUE" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="sep" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="width" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="int" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="snippet" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="snippetArea" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="spaceless" type="PAIR" />
|
||||||
|
<tag name="switch" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="syntax" type="PAIR" arguments="off | double | latte" multiLine="true" />
|
||||||
|
<tag name="templatePrint" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="class-name" types="PHP_CLASS_NAME" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="templateType" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="class-name" types="PHP_CLASS_NAME" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="try" type="PAIR" />
|
||||||
|
<tag name="rollback" type="UNPAIRED" />
|
||||||
|
<tag name="tag" type="ATTR_ONLY">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" validType="string" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="ifchanged" type="PAIR">
|
||||||
|
<arguments>
|
||||||
|
<argument name="expression" types="PHP_EXPRESSION" required="true" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="skipIf" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="var" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="variable" types="VARIABLE_DEFINITION_EXPRESSION" required="true" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="trace" type="UNPAIRED" />
|
||||||
|
<tag name="varPrint" type="UNPAIRED" arguments="all" />
|
||||||
|
<tag name="varType" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="PHP_TYPE" required="true" />
|
||||||
|
<argument name="variable" types="VARIABLE_DEFINITION" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="while" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="condition" types="PHP_CONDITION" validType="bool" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="iterateWhile" type="PAIR" multiLine="true" />
|
||||||
|
<tag name="embed" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="file" types="BLOCK_USAGE,PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="key-value" types="KEY_VALUE" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<!-- @deprecated - latte -->
|
||||||
|
<tag name="assign" type="UNPAIRED" arguments="$variable = expr" />
|
||||||
|
<tag name="truncate" type="UNPAIRED" arguments="expression" deprecatedMessage="Tag {? ...} is deprecated in Latte 2.4. For variable definitions use {var ...} or {php ...} in other cases." />
|
||||||
|
</tags>
|
||||||
|
<filters>
|
||||||
|
<filter name="truncate" arguments=":($length, $append = '…')" description="shortens the length preserving whole words" insertColons=":" />
|
||||||
|
<filter name="substr" arguments=":($offset [, $length])" description="returns part of the string" insertColons=":" />
|
||||||
|
<filter name="trim" arguments=":($charset = mezery)" description="strips whitespace or other characters from the beginning and end of the string" />
|
||||||
|
<filter name="stripHtml" arguments="" description="removes HTML tags and converts HTML entities to text" />
|
||||||
|
<filter name="strip" arguments="" description="removes whitespace" />
|
||||||
|
<filter name="indent" arguments=":($level = 1, $char = '\t')" description="indents the text from left with number of tabs" />
|
||||||
|
<filter name="replace" arguments=":($search, $replace = '')" description="replaces all occurrences of the search string with the replacement" insertColons=":" />
|
||||||
|
<filter name="replaceRE" arguments=":($pattern, $replace = '')" description="replaces all occurrences according to regular expression" insertColons=":" />
|
||||||
|
<filter name="padLeft" arguments=":($length, $pad = ' ')" description="completes the string to given length from left" insertColons=":" />
|
||||||
|
<filter name="padRight" arguments=":($length, $pad = ' ')" description="completes the string to given length from right" insertColons=":" />
|
||||||
|
<filter name="repeat" arguments=":($count)" description="repeats the string" insertColons=":" />
|
||||||
|
<filter name="implode" arguments=":($glue = '')" description="joins an array to a string" />
|
||||||
|
<filter name="webalize" description="adjusts the UTF-8 string to the shape used in the URL" />
|
||||||
|
<filter name="breaklines" description="inserts HTML line breaks before all newlines" />
|
||||||
|
<filter name="reverse" description="reverse an UTF-8 string or array" />
|
||||||
|
<filter name="length" description="returns length of a string or array" />
|
||||||
|
<filter name="sort" description="simply sorts array" />
|
||||||
|
<filter name="reverse" description="array sorted in reverse order (used with |sort)" />
|
||||||
|
<filter name="batch" arguments=":($array, $length [, $item])" description="returns length of a string or array" insertColons="::" />
|
||||||
|
|
||||||
|
<filter name="clamp" description="returns value clamped to the inclusive range of min and max." insertColons="::" />
|
||||||
|
|
||||||
|
<filter name="lower" description="makes a string lower case" />
|
||||||
|
<filter name="upper" description="makes a string upper case" />
|
||||||
|
<filter name="firstUpper" description="makes the first letter upper case" />
|
||||||
|
<filter name="capitalize" description="lower case, the first letter of each word upper case" />
|
||||||
|
|
||||||
|
<filter name="date" arguments=":($format)" description="formats date" insertColons=":" />
|
||||||
|
<filter name="number" arguments=":($decimals = 0, $decPoint = '.', $thousandsSep = ',')" description="format number" />
|
||||||
|
<filter name="bytes" arguments=":($precision = 2)" description="formats size in bytes" />
|
||||||
|
<filter name="dataStream" arguments=":($mimetype = 'detect')" description="Data URI protocol conversion" />
|
||||||
|
|
||||||
|
<filter name="noescape" description="prints a variable without escaping" />
|
||||||
|
<filter name="escapeurl" description="escapes parameter in URL" />
|
||||||
|
|
||||||
|
<filter name="nocheck" description="prevents automatic URL sanitization" />
|
||||||
|
<filter name="checkurl" description="sanitizes string for use inside href attribute" />
|
||||||
|
|
||||||
|
<filter name="query" description="generates a query string in the URL" />
|
||||||
|
<filter name="ceil" arguments=":(int $precision = 0)" description="rounds a number up to a given precision" />
|
||||||
|
<filter name="explode" arguments=":(string $separator = '')" description="splits a string by the given delimiter" />
|
||||||
|
<filter name="first" description="returns first element of array or character of string" />
|
||||||
|
<filter name="floor" arguments=":(int $precision = 0)" description="rounds a number down to a given precision" />
|
||||||
|
<filter name="join" arguments=":(string $glue = '')" description="joins an array to a string" />
|
||||||
|
<filter name="last" description="returns last element of array or character of string" />
|
||||||
|
<filter name="random" description="returns random element of array or character of string" />
|
||||||
|
<filter name="round" arguments=":(int $precision = 0)" description="rounds a number to a given precision" />
|
||||||
|
<filter name="slice" arguments=":(int $start, int $length = null, bool $preserveKeys = false)" description="extracts a slice of an array or a string" insertColons=":" />
|
||||||
|
<filter name="spaceless" description="removes whitespace" />
|
||||||
|
<filter name="split" arguments=":(string $separator = '')" description="splits a string by the given delimiter" />
|
||||||
|
</filters>
|
||||||
|
<functions>
|
||||||
|
<function name="clamp" returnType="int|float" arguments="(int|float $value, int|float $min, int|float $max)" description="clamps value to the inclusive range of min and max" />
|
||||||
|
<function name="divisibleBy" returnType="bool" arguments="(int $value)" description="checks if a variable is divisible by a number" />
|
||||||
|
<function name="even" returnType="bool" arguments="(int $value)" description="checks if the given number is even" />
|
||||||
|
<function name="first" returnType="mixed" arguments="(string|array $value)" description="returns first element of array or character of string" />
|
||||||
|
<function name="last" returnType="mixed" arguments="(string|array $value)" description="returns last element of array or character of string" />
|
||||||
|
<function name="odd" returnType="bool" arguments="(int $value)" description="checks if the given number is odd" />
|
||||||
|
<function name="slice" returnType="string|array" arguments="(string|array $value, int $start, int $length = null, bool $preserveKeys = false)" description="extracts a slice of an array or a string" />
|
||||||
|
</functions>
|
||||||
|
</latte>
|
59
.idea/intellij-latte/xmlSources/NetteApplication.xml
Normal file
59
.idea/intellij-latte/xmlSources/NetteApplication.xml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin configuration XML V0.0.1//EN" "Latte.dtd">
|
||||||
|
<latte version="1" vendor="nette/application">
|
||||||
|
<tags>
|
||||||
|
<!-- nette/application tags -->
|
||||||
|
<tag name="cache" type="PAIR" arguments="if => expr, key, …">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name[:part]" types="KEY_VALUE" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="PHP_EXPRESSION" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="control" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name[:part]" types="PHP_IDENTIFIER,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="PHP_EXPRESSION" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="link" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="href" type="ATTR_ONLY">
|
||||||
|
<arguments>
|
||||||
|
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="nonce" type="ATTR_ONLY" />
|
||||||
|
<tag name="plink" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<!-- @deprecated - nette/application -->
|
||||||
|
<tag name="ifCurrent" type="PAIR" deprecatedMessage="Tag {ifCurrent} is deprecated in Latte 2.6. Use custom function isLinkCurrent() instead.">
|
||||||
|
<arguments>
|
||||||
|
<argument name="destination" types="LINK_DESTINATION,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
<argument name="arguments" types="LINK_PARAMETERS" repeatable="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
</tags>
|
||||||
|
<variables>
|
||||||
|
<variable name="control" type="\Nette\Application\UI\Control" />
|
||||||
|
<variable name="basePath" type="string" />
|
||||||
|
<variable name="baseUrl" type="string" />
|
||||||
|
<variable name="flashes" type="mixed[]" />
|
||||||
|
<variable name="presenter" type="\Nette\Application\UI\Presenter" />
|
||||||
|
<variable name="iterator" type="\Latte\Runtime\CachingIterator" />
|
||||||
|
<variable name="form" type="\Nette\Application\UI\Form" />
|
||||||
|
<variable name="user" type="\Nette\Security\User" />
|
||||||
|
</variables>
|
||||||
|
<functions>
|
||||||
|
<function name="isLinkCurrent" returnType="bool" arguments="(string $destination = null, $args = [])" />
|
||||||
|
<function name="isModuleCurrent" returnType="bool" arguments="(string $moduleName)" />
|
||||||
|
</functions>
|
||||||
|
</latte>
|
41
.idea/intellij-latte/xmlSources/NetteForms.xml
Normal file
41
.idea/intellij-latte/xmlSources/NetteForms.xml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE latte PUBLIC "-//LATTE//Latte plugin configuration XML V0.0.1//EN" "Latte.dtd">
|
||||||
|
<latte version="1" vendor="nette/forms">
|
||||||
|
<tags>
|
||||||
|
<tag name="form" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="formContainer" type="PAIR" multiLine="true">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="formPrint" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="input" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="inputError" type="UNPAIRED">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="label" type="AUTO_EMPTY">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
<tag name="name" type="ATTR_ONLY">
|
||||||
|
<arguments>
|
||||||
|
<argument name="name" types="PHP_IDENTIFIER,VARIABLE,CONTROL,PHP_EXPRESSION" validType="string" required="true" />
|
||||||
|
</arguments>
|
||||||
|
</tag>
|
||||||
|
</tags>
|
||||||
|
</latte>
|
6
.idea/misc.xml
Normal file
6
.idea/misc.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager">
|
||||||
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/openvk.iml" filepath="$PROJECT_DIR$/.idea/openvk.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
46
.idea/openvk.iml
Normal file
46
.idea/openvk.iml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/rybakit/msgpack" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/chillerlan/php-qrcode" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/cache" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/chillerlan/php-settings-container" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/vearutop/php-obscene-censor-rus" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/scssphp/scssphp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/bhaktaraz/php-rss-generator" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/erusev/parsedown" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/ezyang/htmlpurifier" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/whichbrowser/parser" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/komeiji-satori/curl" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/james-heinrich/getid3" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/wapmorgan/binary-stream" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/al/emoji-detector" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/lfkeitel/phptotp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/zadarma/user-api-v1" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/string" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-grapheme" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/vendor/wapmorgan/morphos" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
48
.idea/php.xml
Normal file
48
.idea/php.xml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PhpIncludePathManager">
|
||||||
|
<include_path>
|
||||||
|
<path value="$PROJECT_DIR$/vendor/rybakit/msgpack" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/chillerlan/php-qrcode" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/psr/cache" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/chillerlan/php-settings-container" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/vearutop/php-obscene-censor-rus" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/psr/http-message" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php72" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-normalizer" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/scssphp/scssphp" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/bhaktaraz/php-rss-generator" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/erusev/parsedown" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/ezyang/htmlpurifier" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/whichbrowser/parser" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/komeiji-satori/curl" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/composer" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/james-heinrich/getid3" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/wapmorgan/binary-stream" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/al/emoji-detector" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/lfkeitel/phptotp" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/zadarma/user-api-v1" />
|
||||||
|
<path value="$PROJECT_DIR$/../../../chandler" />
|
||||||
|
<path value="$PROJECT_DIR$/../../../vendor" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-ctype" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/string" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-intl-grapheme" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/deprecation-contracts" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php73" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/psr/container" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/console" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-php80" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/symfony/service-contracts" />
|
||||||
|
<path value="$PROJECT_DIR$/vendor/wapmorgan/morphos" />
|
||||||
|
</include_path>
|
||||||
|
</component>
|
||||||
|
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
|
||||||
|
<option name="suggestChangeDefaultLanguageLevel" value="false" />
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/runConfigurations.xml
Normal file
10
.idea/runConfigurations.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
86
CLI/RebuildImagesCommand.php
Normal file
86
CLI/RebuildImagesCommand.php
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\CLI;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Repositories\Photos;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Nette\Utils\ImageException;
|
||||||
|
|
||||||
|
class RebuildImagesCommand extends Command
|
||||||
|
{
|
||||||
|
private $images;
|
||||||
|
|
||||||
|
protected static $defaultName = "build-images";
|
||||||
|
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->images = DatabaseConnection::i()->getContext()->table("photos");
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->setDescription("Create resized versions of images")
|
||||||
|
->setHelp("This command allows you to resize all your images after configuration change")
|
||||||
|
->addOption("upgrade-only", "U", InputOption::VALUE_NEGATABLE, "Only upgrade images which aren't resized?");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$header = $output->section();
|
||||||
|
$counter = $output->section();
|
||||||
|
|
||||||
|
$header->writeln([
|
||||||
|
"Image Rebuild Utility",
|
||||||
|
"=====================",
|
||||||
|
"",
|
||||||
|
]);
|
||||||
|
|
||||||
|
$filter = ["deleted" => false];
|
||||||
|
if($input->getOption("upgrade-only"))
|
||||||
|
$filter["sizes"] = NULL;
|
||||||
|
|
||||||
|
$selection = $this->images->select("id")->where($filter);
|
||||||
|
$totalPics = $selection->count();
|
||||||
|
$header->writeln([
|
||||||
|
"Total of $totalPics images found.",
|
||||||
|
"",
|
||||||
|
]);
|
||||||
|
|
||||||
|
$errors = 0;
|
||||||
|
$count = 0;
|
||||||
|
$avgTime = NULL;
|
||||||
|
$begin = new \DateTimeImmutable("now");
|
||||||
|
foreach($selection as $idHolder) {
|
||||||
|
$start = microtime(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$photo = (new Photos)->get($idHolder->id);
|
||||||
|
$photo->getSizes(true, true);
|
||||||
|
$photo->getDimensions();
|
||||||
|
} catch(ImageException $ex) {
|
||||||
|
$errors++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeConsumed = microtime(true) - $start;
|
||||||
|
if(!$avgTime)
|
||||||
|
$avgTime = $timeConsumed;
|
||||||
|
else
|
||||||
|
$avgTime = ($avgTime + $timeConsumed) / 2;
|
||||||
|
|
||||||
|
$eta = $begin->getTimestamp() + ceil($totalPics * $avgTime);
|
||||||
|
$int = (new \DateTimeImmutable("now"))->diff(new \DateTimeImmutable("@$eta"));
|
||||||
|
$int = $int->d . "d" . $int->h . "h" . $int->i . "m" . $int->s . "s";
|
||||||
|
$pct = floor(100 * ($count / $totalPics));
|
||||||
|
|
||||||
|
$counter->overwrite("Processed " . ++$count . " images... ($pct% $int left $errors/$count fail)");
|
||||||
|
}
|
||||||
|
|
||||||
|
$counter->overwrite("Processing finished :3");
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
51
README.md
51
README.md
|
@ -6,25 +6,27 @@ _[Русский](README_RU.md)_
|
||||||
|
|
||||||
VKontakte belongs to Pavel Durov and VK Group.
|
VKontakte belongs to Pavel Durov and VK Group.
|
||||||
|
|
||||||
To be honest, we don't even know whether it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug-tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OVK account for this).
|
To be honest, we don't know whether it even works. However, this version is maintained and we will be happy to accept your bugreports [in our bug-tracker](https://github.com/openvk/openvk/projects/1). You should also be able to submit them using [ticketing system](https://openvk.su/support?act=new) (you will need an OVK account for this).
|
||||||
|
|
||||||
## When's the release?
|
## When's the release?
|
||||||
|
|
||||||
Please use the master branch, as it has the most changes.
|
We will release OpenVK as soon as it's ready. As for now you can:
|
||||||
|
* `git clone` this repo's master branch (use `git pull` to update)
|
||||||
Updating the source code is done with this command: `git pull`
|
* Grab a prebuilt OpenVK distro from [GitHub artifacts](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||||
|
|
||||||
## Instances
|
## Instances
|
||||||
|
|
||||||
* **[openvk.su](https://openvk.su/)**
|
* **[openvk.su](https://openvk.su/)**
|
||||||
|
* **[openvk.uk](https://openvk.uk)** - official mirror of openvk.su (<https://t.me/openvkch/1609>)
|
||||||
|
* **[openvk.co](http://openvk.co)** - yet another official mirror of openvk.su without TLS (<https://t.me/openvkch/1654>)
|
||||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||||
* [openvk.zavsc.pw](https://openvk.zavsc.pw/)
|
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||||
|
|
||||||
## Can I create my own OpenVK instance?
|
## Can I create my own OpenVK instance?
|
||||||
|
|
||||||
Yes! And you're very welcome to.
|
Yes! And you're very welcome to.
|
||||||
|
|
||||||
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. this extensions are available on most of ISPManager hostings).
|
However, OVK makes use of Chandler Application Server. This software requires extensions, that may not be provided by your hosting provider (namely, sodium and yaml. these extensions are available on most of ISPManager hostings).
|
||||||
|
|
||||||
If you want, you can add your instance to the list above so that people can register there.
|
If you want, you can add your instance to the list above so that people can register there.
|
||||||
|
|
||||||
|
@ -32,42 +34,47 @@ If you want, you can add your instance to the list above so that people can regi
|
||||||
|
|
||||||
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
1. Install PHP 7.4, web-server, Composer, Node.js, Yarn and [Chandler](https://github.com/openvk/chandler)
|
||||||
|
|
||||||
* PHP 8 has **not** yet been tested, so you should not expect it to work.
|
* PHP 8 has **not** yet been tested, so you should not expect it to work. (edit: it does not work).
|
||||||
|
|
||||||
2. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
2. Install MySQL-compatible database.
|
||||||
|
|
||||||
|
* We recommend using Percona Server, but any MySQL-compatible server should work
|
||||||
|
* Server should be compatible with at least MySQL 5.6, MySQL 8.0+ recommended.
|
||||||
|
* Support for MySQL 4.1+ is WIP, replace `utf8mb4` and `utf8mb4_unicode_520_ci` with `utf8` and `utf8_unicode_ci` in SQLs.
|
||||||
|
|
||||||
|
3. Install [commitcaptcha](https://github.com/openvk/commitcaptcha) and OpenVK as Chandler extensions like this:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
||||||
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
||||||
```
|
```
|
||||||
|
|
||||||
3. And enable them:
|
4. And enable them:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Import `install/init-static-db.sql` to **same database** you installed Chandler to and import all sqls from `install/sqls` to **same database**
|
5. Import `install/init-static-db.sql` to the **same database** you installed Chandler to and import all sqls from `install/sqls` to the **same database**
|
||||||
5. Import `install/init-event-db.sql` to **separate database**
|
6. Import `install/init-event-db.sql` to a **separate database** (Yandex.Clickhouse can also be used, highly recommended)
|
||||||
6. Copy `openvk-example.yml` to `openvk.yml` and change options
|
7. Copy `openvk-example.yml` to `openvk.yml` and change options to your liking
|
||||||
7. Run `composer install` in OpenVK directory
|
8. Run `composer install` in OpenVK directory
|
||||||
8. Move to `Web/static/js` and execute `yarn install`
|
9. Run `composer install` in commitcaptcha directory
|
||||||
9. Set `openvk` as your root app in `chandler.yml`
|
10. Move to `Web/static/js` and execute `yarn install`
|
||||||
|
11. Set `openvk` as your root app in `chandler.yml`
|
||||||
|
|
||||||
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
Once you are done, you can login as a system administrator on the network itself (no registration required):
|
||||||
|
|
||||||
* **Login**: `admin@localhost.localdomain6`
|
* **Login**: `admin@localhost.localdomain6`
|
||||||
* **Password**: `admin`
|
* **Password**: `admin`
|
||||||
* It is recommended to change the password before using the built-in account.
|
* It is recommended to change the password of the built-in account or disable it.
|
||||||
|
|
||||||
Full example installation instruction for CentOS 8 is also available [here](https://docs.openvk.su/openvk_engine/centos8_installation/).
|
💡Confused? Full installation walkthrough is available [here](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [and](https://almalinux.org/) [family](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||||
|
|
||||||
### If my website uses OpenVK, should I publish it's sources?
|
### If my website uses OpenVK, should I release it's sources?
|
||||||
|
|
||||||
You are encouraged to do so. We don't enforce this though. You can keep your sources to yourself (unless you distribute your OpenVK distro to other people).
|
It depends. You can keep the sources to yourself if you do not plan to distribute your website binaries. If your website software must be distributed, it can stay non-OSS provided the OpenVK is not used as a primary application and is not modified. If you modified OpenVK for your needs or your work is based on it and you're planning to redistribute this, then you should license it under terms of any LGPL-compatible license (like OSL, GPL, LGPL etc).
|
||||||
|
|
||||||
You also not required to publish source texts of your themepacks and plugins.
|
|
||||||
|
|
||||||
## Where can I get assistance?
|
## Where can I get assistance?
|
||||||
|
|
||||||
|
@ -80,7 +87,7 @@ You may reach out to us via:
|
||||||
* [Discussions](https://github.com/openvk/openvk/discussions)
|
* [Discussions](https://github.com/openvk/openvk/discussions)
|
||||||
* Matrix chat: #openvk:matrix.org
|
* Matrix chat: #openvk:matrix.org
|
||||||
|
|
||||||
**Attention**: bug tracker, telegram and matrix chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com**
|
**Attention**: bug tracker, board, telegram and matrix chat are public places. And ticketing system is being served by volunteers. If you need to report something, that shouldn't be immediately disclosed to general public (for instance, vulnerability report), please use contact us directly at this email: **openvk [at] tutanota [dot] com**
|
||||||
|
|
||||||
<a href="https://codeberg.org/OpenVK/openvk">
|
<a href="https://codeberg.org/OpenVK/openvk">
|
||||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||||
|
|
49
README_RU.md
49
README_RU.md
|
@ -2,23 +2,25 @@
|
||||||
|
|
||||||
_[English](README.md)_
|
_[English](README.md)_
|
||||||
|
|
||||||
**OpenVK** это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. Представленный здесь код пока не стабилен.
|
**OpenVK** - это попытка создать простую CMS, которая ~~косплеит~~ имитирует старый ВКонтакте. На данный момент представленный здесь исходный код проекта пока не является стабильным.
|
||||||
|
|
||||||
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
ВКонтакте принадлежит Павлу Дурову и VK Group.
|
||||||
|
|
||||||
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OVK).
|
Честно говоря, мы даже не знаем, работает ли она вообще. Однако, эта версия поддерживается, и мы будем рады принять ваши сообщения об ошибках [в нашем баг-трекере](https://github.com/openvk/openvk/projects/1). Вы также можете отправлять их через [вкладку "Помощь"](https://openvk.su/support?act=new) (для этого вам понадобится учетная запись OVK).
|
||||||
|
|
||||||
## Когда релиз?
|
## Когда выйдет релизная версия?
|
||||||
|
|
||||||
Пожалуйста, используйте ветку master, так как в ней больше всего изменений.
|
Мы выпустим OpenVK, как только он будет готов. На данный момент Вы можете:
|
||||||
|
* Склонировать master ветку репозитория командой `git clone` (используйте `git pull` для обновления)
|
||||||
Обновление исходного кода выполняется с помощью этой команды: `git pull`.
|
* Взять готовую сборку OpenVK из [GitHub Actions](https://nightly.link/openvk/archive/workflows/nightly/master/OpenVK%20Archive.zip)
|
||||||
|
|
||||||
## Инстанции
|
## Инстанции
|
||||||
|
|
||||||
* **[openvk.su](https://openvk.su/)**
|
* **[openvk.su](https://openvk.su/)**
|
||||||
|
* **[openvk.uk](https://openvk.uk)** - официальное зеркало openvk.su (<https://t.me/openvkch/1609>)
|
||||||
|
* **[openvk.co](http://openvk.co)** - ещё одно официальное зеркало openvk.su без TLS (<https://t.me/openvkch/1654>)
|
||||||
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
* [social.fetbuk.ru](http://social.fetbuk.ru/)
|
||||||
* [openvk.zavsc.pw](https://openvk.zavsc.pw/)
|
* [vepurovk.xyz](http://vepurovk.xyz/)
|
||||||
|
|
||||||
## Могу ли я создать свою собственную инстанцию OpenVK?
|
## Могу ли я создать свою собственную инстанцию OpenVK?
|
||||||
|
|
||||||
|
@ -32,42 +34,47 @@ _[English](README.md)_
|
||||||
|
|
||||||
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
1. Установите PHP 7.4, веб-сервер, Composer, Node.js, Yarn и [Chandler](https://github.com/openvk/chandler)
|
||||||
|
|
||||||
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать.
|
* PHP 8 еще **не** тестировался, поэтому не стоит ожидать, что он будет работать (UPD: он не работает).
|
||||||
|
|
||||||
2. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler следующим образом:
|
2. Установите MySQL-совместимую базу данных.
|
||||||
|
|
||||||
|
* Мы рекомендуем использовать Persona Server, но любая MySQL-совместимая база данных должна работать
|
||||||
|
* Сервер должен поддерживать хотя бы MySQL 5.6, рекомендуется использовать MySQL 8.0+.
|
||||||
|
* Поддержка для MySQL 4.1+ находится в процессе, а пока замените `utf8mb4` и `utf8mb4_unicode_520_ci` на `utf8` и `utf8_unicode_ci` в SQL-файлах, соответственно.
|
||||||
|
|
||||||
|
3. Установите [commitcaptcha](https://github.com/openvk/commitcaptcha) и OpenVK в качестве расширений Chandler:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
git clone https://github.com/openvk/openvk /path/to/chandler/extensions/available/openvk
|
||||||
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
git clone https://github.com/openvk/commitcaptcha /path/to/chandler/extensions/available/commitcaptcha
|
||||||
```
|
```
|
||||||
|
|
||||||
3. И включите их:
|
4. И включите их:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
ln -s /path/to/chandler/extensions/available/commitcaptcha /path/to/chandler/extensions/enabled/
|
||||||
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions/enabled/
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных**
|
5. Импортируйте `install/init-static-db.sql` в **ту же базу данных**, в которую вы установили Chandler, и импортируйте все SQL файлы из папки `install/sqls` в **ту же базу данных**
|
||||||
5. Импортируйте `install/init-event-db.sql` в **отдельную базу данных**
|
6. Импортируйте `install/init-event-db.sql` в **отдельную базу данных** (Яндекс.Clickhouse также может быть использован, настоятельно рекомендуется)
|
||||||
6. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры
|
7. Скопируйте `openvk-example.yml` в `openvk.yml` и измените параметры под свои нужды
|
||||||
7. Запустите `composer install` в директории OpenVK
|
8. Запустите `composer install` в директории OpenVK
|
||||||
8. Перейдите в `Web/static/js` и выполните `yarn install`
|
9. Запустите `composer install` в директории commitcaptcha
|
||||||
9. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
|
10. Перейдите в `Web/static/js` и выполните `yarn install`
|
||||||
|
11. Установите `openvk` в качестве корневого приложения в файле `chandler.yml`
|
||||||
|
|
||||||
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):
|
После этого вы можете войти как системный администратор в саму сеть (регистрация не требуется):
|
||||||
|
|
||||||
* **Логин**: `admin@localhost.localdomain6`
|
* **Логин**: `admin@localhost.localdomain6`
|
||||||
* **Пароль**: `admin`
|
* **Пароль**: `admin`
|
||||||
* Перед использованием встроенной учетной записи рекомендуется сменить пароль.
|
* Перед использованием встроенной учетной записи рекомендуется сменить пароль или отключить её.
|
||||||
|
|
||||||
Полный пример инструкции по установке CentOS 8 также доступен [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/).
|
💡Запутались? Полное руководство по установке доступно [здесь](https://docs.openvk.su/openvk_engine/centos8_installation/) (CentOS 8 [и](https://almalinux.org/ru/) [семейство](https://yum.oracle.com/oracle-linux-isos.html)).
|
||||||
|
|
||||||
### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты?
|
### Если мой сайт использует OpenVK, должен ли я публиковать его исходные тексты?
|
||||||
|
|
||||||
Вам рекомендуется это делать. Однако мы не следим за этим. Вы можете держать свои исходные тексты при себе (если только вы не распространяете свой дистрибутив OpenVK среди других людей).
|
Это зависит от обстоятельств. Вы можете оставить исходные тексты при себе, если не планируете распространять бинарники вашего сайта. Если программное обеспечение вашего сайта должно распространяться, оно может оставаться не-OSS при условии, что OpenVK не используется в качестве основного приложения и не модифицируется. Если вы модифицировали OpenVK для своих нужд или ваша работа основана на нем и вы планируете ее распространять, то вы должны лицензировать ее на условиях любой совместимой с LGPL лицензии (например, OSL, GPL, LGPL и т.д.).
|
||||||
|
|
||||||
Вы также не обязаны публиковать исходные тексты ваших тематических пакетов и плагинов.
|
|
||||||
|
|
||||||
## Где я могу получить помощь?
|
## Где я могу получить помощь?
|
||||||
|
|
||||||
|
@ -80,7 +87,7 @@ ln -s /path/to/chandler/extensions/available/openvk /path/to/chandler/extensions
|
||||||
* [Обсуждения](https://github.com/openvk/openvk/discussions)
|
* [Обсуждения](https://github.com/openvk/openvk/discussions)
|
||||||
* Чат в Matrix: #ovk:matrix.org
|
* Чат в Matrix: #ovk:matrix.org
|
||||||
|
|
||||||
**Внимание**: баг-трекер, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [at] tutanota [dot] com**.
|
**Внимание**: баг-трекер, форум, телеграм- и matrix-чат являются публичными местами, и жалобы в OVK обслуживается волонтерами. Если вам нужно сообщить о чем-то, что не должно быть раскрыто широкой публике (например, сообщение об уязвимости), пожалуйста, свяжитесь с нами напрямую по этому адресу: **openvk [собака] tutanota [точка] com**.
|
||||||
|
|
||||||
<a href="https://codeberg.org/OpenVK/openvk">
|
<a href="https://codeberg.org/OpenVK/openvk">
|
||||||
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
<img alt="Get it on Codeberg" src="https://codeberg.org/Codeberg/GetItOnCodeberg/media/branch/main/get-it-on-blue-on-white.png" height="60">
|
||||||
|
|
|
@ -60,7 +60,7 @@ final class Account extends VKAPIRequestHandler
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppPermissions(): object
|
function getAppPermissions(): int
|
||||||
{
|
{
|
||||||
return 9355263;
|
return 9355263;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ final class Friends extends VKAPIRequestHandler
|
||||||
$usersApi = new Users($this->getUser());
|
$usersApi = new Users($this->getUser());
|
||||||
|
|
||||||
if (!is_null($fields)) {
|
if (!is_null($fields)) {
|
||||||
$response = $usersApi->get(implode(',', $friends), $fields, 0, $count, true); // FIXME
|
$response = $usersApi->get(implode(',', $friends), $fields, 0, $count); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
|
|
|
@ -88,4 +88,100 @@ final class Groups extends VKAPIRequestHandler
|
||||||
"items" => $rClubs
|
"items" => $rClubs
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getById(string $group_ids = "", string $group_id = "", string $fields = ""): ?array
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$clubs = new ClubsRepo;
|
||||||
|
|
||||||
|
if ($group_ids == null && $group_id != null)
|
||||||
|
$group_ids = $group_id;
|
||||||
|
|
||||||
|
if ($group_ids == null && $group_id == null)
|
||||||
|
$this->fail(100, "One of the parameters specified was missing or invalid: group_ids is undefined");
|
||||||
|
|
||||||
|
$clbs = explode(',', $group_ids);
|
||||||
|
$response;
|
||||||
|
|
||||||
|
$ic = sizeof($clbs);
|
||||||
|
|
||||||
|
for ($i=0; $i < $ic; $i++) {
|
||||||
|
if($i > 500)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if($clbs[$i] < 0)
|
||||||
|
$this->fail(100, "ты ошибся чутка, у айди группы убери минус");
|
||||||
|
|
||||||
|
$clb = $clubs->get((int) $clbs[$i]);
|
||||||
|
if(is_null($clb))
|
||||||
|
{
|
||||||
|
$response[$i] = (object)[
|
||||||
|
"id" => intval($clbs[$i]),
|
||||||
|
"name" => "DELETED",
|
||||||
|
"screen_name" => "club".intval($clbs[$i]),
|
||||||
|
"type" => "group",
|
||||||
|
"description" => "This group was deleted or it doesn't exist"
|
||||||
|
];
|
||||||
|
}else if($clbs[$i] == null){
|
||||||
|
|
||||||
|
}else{
|
||||||
|
$response[$i] = (object)[
|
||||||
|
"id" => $clb->getId(),
|
||||||
|
"name" => $clb->getName(),
|
||||||
|
"screen_name" => $clb->getShortCode() ?? "club".$clb->getId(),
|
||||||
|
"is_closed" => false,
|
||||||
|
"type" => "group",
|
||||||
|
"can_access_closed" => true,
|
||||||
|
];
|
||||||
|
|
||||||
|
$flds = explode(',', $fields);
|
||||||
|
|
||||||
|
foreach($flds as $field) {
|
||||||
|
switch ($field) {
|
||||||
|
case 'verified':
|
||||||
|
$response[$i]->verified = intval($clb->isVerified());
|
||||||
|
break;
|
||||||
|
case 'has_photo':
|
||||||
|
$response[$i]->has_photo = is_null($clb->getAvatarPhoto()) ? 0 : 1;
|
||||||
|
break;
|
||||||
|
case 'photo_max_orig':
|
||||||
|
$response[$i]->photo_max_orig = $clb->getAvatarURL();
|
||||||
|
break;
|
||||||
|
case 'photo_max':
|
||||||
|
$response[$i]->photo_max = $clb->getAvatarURL();
|
||||||
|
break;
|
||||||
|
case 'members_count':
|
||||||
|
$response[$i]->members_count = $clb->getFollowersCount();
|
||||||
|
break;
|
||||||
|
case 'site':
|
||||||
|
$response[$i]->site = $clb->getWebsite();
|
||||||
|
break;
|
||||||
|
case 'description':
|
||||||
|
$response[$i]->desctiption = $clb->getDescription();
|
||||||
|
break;
|
||||||
|
case 'contacts':
|
||||||
|
$contacts;
|
||||||
|
$contactTmp = $clb->getManagers(1, true);
|
||||||
|
foreach($contactTmp as $contact) {
|
||||||
|
$contacts[] = array(
|
||||||
|
'user_id' => $contact->getUser()->getId(),
|
||||||
|
'desc' => $contact->getComment()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$response[$i]->contacts = $contacts;
|
||||||
|
break;
|
||||||
|
case 'can_post':
|
||||||
|
if($clb->canBeModifiedBy($this->getUser()))
|
||||||
|
$response[$i]->can_post = true;
|
||||||
|
else
|
||||||
|
$response[$i]->can_post = $clb->canPost();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
74
VKAPI/Handlers/Likes.php
Normal file
74
VKAPI/Handlers/Likes.php
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
|
|
||||||
|
final class Likes extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function add(string $type, int $owner_id, int $item_id): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'post':
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
|
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||||
|
|
||||||
|
$post->setLike(true, $this->getUser());
|
||||||
|
return (object)[
|
||||||
|
"likes" => $post->getLikesCount()
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(string $type, int $owner_id, int $item_id): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'post':
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
|
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||||
|
|
||||||
|
$post->setLike(false, $this->getUser());
|
||||||
|
return (object)[
|
||||||
|
"likes" => $post->getLikesCount()
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLiked(int $user_id, string $type, int $owner_id, int $item_id): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'post':
|
||||||
|
$user = (new UsersRepo)->get($user_id);
|
||||||
|
if (is_null($user)) return (object)[
|
||||||
|
"liked" => 0,
|
||||||
|
"copied" => 0,
|
||||||
|
"sex" => 0
|
||||||
|
];
|
||||||
|
|
||||||
|
$post = (new PostsRepo)->getPostById($owner_id, $item_id);
|
||||||
|
if (is_null($post)) $this->fail(100, 'One of the parameters specified was missing or invalid: object not found');
|
||||||
|
|
||||||
|
return (object)[
|
||||||
|
"liked" => (int) $post->hasLikeFrom($user),
|
||||||
|
"copied" => 0 // TODO: handle this
|
||||||
|
];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->fail(100, 'One of the parameters specified was missing or invalid: incorrect type');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ use openvk\Web\Events\NewMessageEvent;
|
||||||
use openvk\Web\Models\Entities\{Correspondence, Message};
|
use openvk\Web\Models\Entities\{Correspondence, Message};
|
||||||
use openvk\Web\Models\Repositories\{Messages as MSGRepo, Users as USRRepo};
|
use openvk\Web\Models\Repositories\{Messages as MSGRepo, Users as USRRepo};
|
||||||
use openvk\VKAPI\Structures\{Message as APIMsg, Conversation as APIConvo};
|
use openvk\VKAPI\Structures\{Message as APIMsg, Conversation as APIConvo};
|
||||||
|
use openvk\VKAPI\Handlers\Users as APIUsers;
|
||||||
use Chandler\Signaling\SignalManager;
|
use Chandler\Signaling\SignalManager;
|
||||||
|
|
||||||
final class Messages extends VKAPIRequestHandler
|
final class Messages extends VKAPIRequestHandler
|
||||||
|
@ -48,10 +49,12 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$rMsg->read_state = 1;
|
$rMsg->read_state = 1;
|
||||||
$rMsg->out = (int) ($message->getSender()->getId() === $this->getUser()->getId());
|
$rMsg->out = (int) ($message->getSender()->getId() === $this->getUser()->getId());
|
||||||
$rMsg->body = $message->getText(false);
|
$rMsg->body = $message->getText(false);
|
||||||
|
$rMsg->text = $message->getText(false);
|
||||||
$rMsg->emoji = true;
|
$rMsg->emoji = true;
|
||||||
|
|
||||||
if($preview_length > 0)
|
if($preview_length > 0)
|
||||||
$rMsg->body = ovk_proc_strtr($rMsg->body, $preview_length);
|
$rMsg->body = ovk_proc_strtr($rMsg->body, $preview_length);
|
||||||
|
$rMsg->text = ovk_proc_strtr($rMsg->text, $preview_length);
|
||||||
|
|
||||||
$items[] = $rMsg;
|
$items[] = $rMsg;
|
||||||
}
|
}
|
||||||
|
@ -145,12 +148,14 @@ final class Messages extends VKAPIRequestHandler
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConversations(int $offset = 0, int $count = 20, string $filter = "all", int $extended = 0): object
|
function getConversations(int $offset = 0, int $count = 20, string $filter = "all", int $extended = 0, string $fields = ""): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
||||||
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
|
$convos = (new MSGRepo)->getCorrespondencies($this->getUser(), -1, $count, $offset);
|
||||||
$list = [];
|
$list = [];
|
||||||
|
|
||||||
|
$users = [];
|
||||||
foreach($convos as $convo) {
|
foreach($convos as $convo) {
|
||||||
$correspondents = $convo->getCorrespondents();
|
$correspondents = $convo->getCorrespondents();
|
||||||
if($correspondents[0]->getId() === $this->getUser()->getId())
|
if($correspondents[0]->getId() === $this->getUser()->getId())
|
||||||
|
@ -189,7 +194,13 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$lastMessagePreview->read_state = 1;
|
$lastMessagePreview->read_state = 1;
|
||||||
$lastMessagePreview->out = (int) ($lastMessage->getSender()->getId() === $this->getUser()->getId());
|
$lastMessagePreview->out = (int) ($lastMessage->getSender()->getId() === $this->getUser()->getId());
|
||||||
$lastMessagePreview->body = $lastMessage->getText(false);
|
$lastMessagePreview->body = $lastMessage->getText(false);
|
||||||
|
$lastMessagePreview->text = $lastMessage->getText(false);
|
||||||
$lastMessagePreview->emoji = true;
|
$lastMessagePreview->emoji = true;
|
||||||
|
|
||||||
|
if($extended == 1) {
|
||||||
|
$users[] = $lastMessage->getSender()->getId();
|
||||||
|
$users[] = $author;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$list[] = [
|
$list[] = [
|
||||||
|
@ -198,10 +209,20 @@ final class Messages extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object) [
|
if($extended == 0){
|
||||||
"count" => sizeof($list),
|
return (object) [
|
||||||
"items" => $list,
|
"count" => sizeof($list),
|
||||||
];
|
"items" => $list,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$users = array_unique($users);
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($list),
|
||||||
|
"items" => $list,
|
||||||
|
"profiles" => (new APIUsers)->get(implode(',', $users), $fields, $offset, $count)
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0): object
|
function getHistory(int $offset = 0, int $count = 20, int $user_id = -1, int $peer_id = -1, int $start_message_id = 0, int $rev = 0, int $extended = 0): object
|
||||||
|
@ -230,6 +251,7 @@ final class Messages extends VKAPIRequestHandler
|
||||||
$rMsg->read_state = 1;
|
$rMsg->read_state = 1;
|
||||||
$rMsg->out = (int) ($msgU->sender_id === $this->getUser()->getId());
|
$rMsg->out = (int) ($msgU->sender_id === $this->getUser()->getId());
|
||||||
$rMsg->body = $message->getText(false);
|
$rMsg->body = $message->getText(false);
|
||||||
|
$rMsg->text = $message->getText(false);
|
||||||
$rMsg->emoji = true;
|
$rMsg->emoji = true;
|
||||||
|
|
||||||
$results[] = $rMsg;
|
$results[] = $rMsg;
|
||||||
|
|
42
VKAPI/Handlers/Newsfeed.php
Normal file
42
VKAPI/Handlers/Newsfeed.php
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
use openvk\Web\Models\Entities\User;
|
||||||
|
use openvk\Web\Models\Entities\Post;
|
||||||
|
use openvk\Web\Models\Entities\Postable;
|
||||||
|
use Chandler\Database\DatabaseConnection;
|
||||||
|
use openvk\Web\Models\Repositories\Posts as PostsRepo;
|
||||||
|
use openvk\VKAPI\Handlers\Wall;
|
||||||
|
|
||||||
|
final class Newsfeed extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
function get(string $fields = "", int $start_from = 0, int $offset = 0, int $count = 30, int $extended = 0)
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
if($offset != 0) $start_from = $offset;
|
||||||
|
|
||||||
|
$id = $this->getUser()->getId();
|
||||||
|
$subs = DatabaseConnection::i()
|
||||||
|
->getContext()
|
||||||
|
->table("subscriptions")
|
||||||
|
->where("follower", $id);
|
||||||
|
$ids = array_map(function($rel) {
|
||||||
|
return $rel->target * ($rel->model === "openvk\Web\Models\Entities\User" ? 1 : -1);
|
||||||
|
}, iterator_to_array($subs));
|
||||||
|
$ids[] = $this->getUser()->getId();
|
||||||
|
|
||||||
|
$posts = DatabaseConnection::i()
|
||||||
|
->getContext()
|
||||||
|
->table("posts")
|
||||||
|
->select("id")
|
||||||
|
->where("wall IN (?)", $ids)
|
||||||
|
->where("deleted", 0)
|
||||||
|
->order("created DESC");
|
||||||
|
|
||||||
|
$rposts = [];
|
||||||
|
foreach($posts->page((int) ($offset + 1), $count) as $post)
|
||||||
|
$rposts[] = (new PostsRepo)->get($post->id)->getPrettyId();
|
||||||
|
|
||||||
|
return (new Wall)->getById(implode(',', $rposts), $extended, $fields, $this->getUser());
|
||||||
|
}
|
||||||
|
}
|
230
VKAPI/Handlers/Photos.php
Normal file
230
VKAPI/Handlers/Photos.php
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
namespace openvk\VKAPI\Handlers;
|
||||||
|
|
||||||
|
use Nette\InvalidStateException;
|
||||||
|
use Nette\Utils\ImageException;
|
||||||
|
use openvk\Web\Models\Entities\Photo;
|
||||||
|
use openvk\Web\Models\Repositories\Albums;
|
||||||
|
use openvk\Web\Models\Repositories\Clubs;
|
||||||
|
|
||||||
|
final class Photos extends VKAPIRequestHandler
|
||||||
|
{
|
||||||
|
private function getPhotoUploadUrl(string $field, int $group = 0, bool $multifile = false): string
|
||||||
|
{
|
||||||
|
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
||||||
|
$uploadInfo = [
|
||||||
|
1,
|
||||||
|
$field,
|
||||||
|
(int) $multifile,
|
||||||
|
0,
|
||||||
|
time(),
|
||||||
|
$this->getUser()->getId(),
|
||||||
|
$group,
|
||||||
|
0, # this is unused but stays here base64 reasons (X2 doesn't work, so there's dummy value for short)
|
||||||
|
];
|
||||||
|
$uploadInfo = pack("vZ10v2P3S", ...$uploadInfo);
|
||||||
|
$uploadInfo = base64_encode($uploadInfo);
|
||||||
|
$uploadHash = hash_hmac("sha3-224", $uploadInfo, $secret);
|
||||||
|
$uploadInfo = rawurlencode($uploadInfo);
|
||||||
|
|
||||||
|
return ovk_scheme(true) . $_SERVER["HTTP_HOST"] . "/upload/photo/$uploadHash?$uploadInfo";
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getImagePath(string $photo, string $hash, ?string& $up = NULL, ?string& $group = NULL): string
|
||||||
|
{
|
||||||
|
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
||||||
|
if(!hash_equals(hash_hmac("sha3-224", $photo, $secret), $hash))
|
||||||
|
$this->fail(121, "Incorrect hash");
|
||||||
|
|
||||||
|
[$up, $image, $group] = explode("|", $photo);
|
||||||
|
|
||||||
|
$imagePath = __DIR__ . "/../../tmp/api-storage/photos/$up" . "_$image.oct";
|
||||||
|
if(!file_exists($imagePath))
|
||||||
|
$this->fail(10, "Invalid image");
|
||||||
|
|
||||||
|
return $imagePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwnerPhotoUploadServer(int $owner_id = 0): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
if($owner_id < 0) {
|
||||||
|
$club = (new Clubs)->get(abs($owner_id));
|
||||||
|
if(!$club)
|
||||||
|
$this->fail(0404, "Club not found");
|
||||||
|
else if(!$club->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(200, "Access: Club can't be 'written' by user");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"upload_url" => $this->getPhotoUploadUrl("photo", isset($club) ? 0 : $club->getId()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveOwnerPhoto(string $photo, string $hash): object
|
||||||
|
{
|
||||||
|
$imagePath = $this->getImagePath($photo, $hash, $uploader, $group);
|
||||||
|
if($group == 0) {
|
||||||
|
$user = (new \openvk\Web\Models\Repositories\Users)->get((int) $uploader);
|
||||||
|
$album = (new Albums)->getUserAvatarAlbum($user);
|
||||||
|
} else {
|
||||||
|
$club = (new Clubs)->get((int) $group);
|
||||||
|
$album = (new Albums)->getClubAvatarAlbum($club);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$avatar = new Photo;
|
||||||
|
$avatar->setOwner((int) $uploader);
|
||||||
|
$avatar->setDescription("Profile photo");
|
||||||
|
$avatar->setCreated(time());
|
||||||
|
$avatar->setFile([
|
||||||
|
"tmp_name" => $imagePath,
|
||||||
|
"error" => 0,
|
||||||
|
]);
|
||||||
|
$avatar->save();
|
||||||
|
$album->addPhoto($avatar);
|
||||||
|
unlink($imagePath);
|
||||||
|
} catch(ImageException | InvalidStateException $e) {
|
||||||
|
unlink($imagePath);
|
||||||
|
$this->fail(129, "Invalid image file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"photo_hash" => NULL,
|
||||||
|
"photo_src" => $avatar->getURL(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWallUploadServer(?int $group_id = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$album = NULL;
|
||||||
|
if(!is_null($group_id)) {
|
||||||
|
$club = (new Clubs)->get(abs($group_id));
|
||||||
|
if(!$club)
|
||||||
|
$this->fail(0404, "Club not found");
|
||||||
|
else if(!$club->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(200, "Access: Club can't be 'written' by user");
|
||||||
|
} else {
|
||||||
|
$album = (new Albums)->getUserWallAlbum($this->getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"upload_url" => $this->getPhotoUploadUrl("photo", $group_id ?? 0),
|
||||||
|
"album_id" => $album,
|
||||||
|
"user_id" => $this->getUser()->getId(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveWallPhoto(string $photo, string $hash, int $group_id = 0, ?string $caption = NULL): array
|
||||||
|
{
|
||||||
|
$imagePath = $this->getImagePath($photo, $hash, $uploader, $group);
|
||||||
|
if($group_id != $group)
|
||||||
|
$this->fail(8, "group_id doesn't match");
|
||||||
|
|
||||||
|
$album = NULL;
|
||||||
|
if($group_id != 0) {
|
||||||
|
$uploader = (new \openvk\Web\Models\Repositories\Users)->get((int) $uploader);
|
||||||
|
$album = (new Albums)->getUserWallAlbum($uploader);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$photo = new Photo;
|
||||||
|
$photo->setOwner((int) $uploader);
|
||||||
|
$photo->setCreated(time());
|
||||||
|
$photo->setFile([
|
||||||
|
"tmp_name" => $imagePath,
|
||||||
|
"error" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!is_null($caption))
|
||||||
|
$photo->setDescription($caption);
|
||||||
|
|
||||||
|
$photo->save();
|
||||||
|
unlink($imagePath);
|
||||||
|
} catch(ImageException | InvalidStateException $e) {
|
||||||
|
unlink($imagePath);
|
||||||
|
$this->fail(129, "Invalid image file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!is_null($album))
|
||||||
|
$album->addPhoto($photo);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$photo->toVkApiStruct(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUploadServer(?int $album_id = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
# Not checking rights to album because save() method will do so anyways
|
||||||
|
return (object) [
|
||||||
|
"upload_url" => $this->getPhotoUploadUrl("photo", 0, true),
|
||||||
|
"album_id" => $album_id,
|
||||||
|
"user_id" => $this->getUser()->getId(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(string $photos_list, string $hash, int $album_id = 0, ?string $caption = NULL): object
|
||||||
|
{
|
||||||
|
$this->requireUser();
|
||||||
|
|
||||||
|
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
||||||
|
if(!hash_equals(hash_hmac("sha3-224", $photos_list, $secret), $hash))
|
||||||
|
$this->fail(121, "Incorrect hash");
|
||||||
|
|
||||||
|
$album = NULL;
|
||||||
|
if($album_id != 0) {
|
||||||
|
$album_ = (new Albums)->get($album_id);
|
||||||
|
if(!$album_)
|
||||||
|
$this->fail(0404, "Invalid album");
|
||||||
|
else if(!$album_->canBeModifiedBy($this->getUser()))
|
||||||
|
$this->fail(15, "Access: Album can't be 'written' by user");
|
||||||
|
|
||||||
|
$album = $album_;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pList = json_decode($photos_list);
|
||||||
|
$imagePaths = [];
|
||||||
|
foreach($pList as $pDesc)
|
||||||
|
$imagePaths[] = __DIR__ . "/../../tmp/api-storage/photos/$pDesc->keyholder" . "_$pDesc->resource.oct";
|
||||||
|
|
||||||
|
$images = [];
|
||||||
|
try {
|
||||||
|
foreach($imagePaths as $imagePath) {
|
||||||
|
$photo = new Photo;
|
||||||
|
$photo->setOwner($this->getUser()->getId());
|
||||||
|
$photo->setCreated(time());
|
||||||
|
$photo->setFile([
|
||||||
|
"tmp_name" => $imagePath,
|
||||||
|
"error" => 0,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!is_null($caption))
|
||||||
|
$photo->setDescription($caption);
|
||||||
|
|
||||||
|
$photo->save();
|
||||||
|
unlink($imagePath);
|
||||||
|
|
||||||
|
if(!is_null($album))
|
||||||
|
$album->addPhoto($photo);
|
||||||
|
|
||||||
|
$images[] = $photo->toVkApiStruct();
|
||||||
|
}
|
||||||
|
} catch(ImageException | InvalidStateException $e) {
|
||||||
|
foreach($imagePaths as $imagePath)
|
||||||
|
unlink($imagePath);
|
||||||
|
|
||||||
|
$this->fail(129, "Invalid image file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object) [
|
||||||
|
"count" => sizeof($images),
|
||||||
|
"items" => $images,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,13 +5,15 @@ use openvk\Web\Models\Repositories\Users as UsersRepo;
|
||||||
|
|
||||||
final class Users extends VKAPIRequestHandler
|
final class Users extends VKAPIRequestHandler
|
||||||
{
|
{
|
||||||
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100): array
|
function get(string $user_ids = "0", string $fields = "", int $offset = 0, int $count = 100, User $authuser = null /* костыль(( */): array
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
// $this->requireUser();
|
||||||
|
|
||||||
|
if($authuser == null) $authuser = $this->getUser();
|
||||||
|
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
if($user_ids == "0")
|
if($user_ids == "0")
|
||||||
$user_ids = (string) $this->getUser()->getId();
|
$user_ids = (string) $authuser->getId();
|
||||||
|
|
||||||
$usrs = explode(',', $user_ids);
|
$usrs = explode(',', $user_ids);
|
||||||
$response;
|
$response;
|
||||||
|
@ -51,7 +53,7 @@ final class Users extends VKAPIRequestHandler
|
||||||
$response[$i]->verified = intval($usr->isVerified());
|
$response[$i]->verified = intval($usr->isVerified());
|
||||||
break;
|
break;
|
||||||
case 'sex':
|
case 'sex':
|
||||||
$response[$i]->sex = $this->getUser()->isFemale() ? 1 : 2;
|
$response[$i]->sex = $usr->isFemale() ? 1 : 2;
|
||||||
break;
|
break;
|
||||||
case 'has_photo':
|
case 'has_photo':
|
||||||
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
$response[$i]->has_photo = is_null($usr->getAvatarPhoto()) ? 0 : 1;
|
||||||
|
@ -60,8 +62,26 @@ final class Users extends VKAPIRequestHandler
|
||||||
$response[$i]->photo_max_orig = $usr->getAvatarURL();
|
$response[$i]->photo_max_orig = $usr->getAvatarURL();
|
||||||
break;
|
break;
|
||||||
case 'photo_max':
|
case 'photo_max':
|
||||||
$response[$i]->photo_max = $usr->getAvatarURL();
|
$response[$i]->photo_max = $usr->getAvatarURL("original");
|
||||||
break;
|
break;
|
||||||
|
case 'photo_50':
|
||||||
|
$response[$i]->photo_50 = $usr->getAvatarURL();
|
||||||
|
break;
|
||||||
|
case 'photo_100':
|
||||||
|
$response[$i]->photo_50 = $usr->getAvatarURL("tiny");
|
||||||
|
break;
|
||||||
|
case 'photo_200':
|
||||||
|
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
|
||||||
|
break;
|
||||||
|
case 'photo_200_orig': // вообще не ебу к чему эта строка ну пусть будет кек
|
||||||
|
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
|
||||||
|
break;
|
||||||
|
case 'photo_400_orig':
|
||||||
|
$response[$i]->photo_50 = $usr->getAvatarURL("normal");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Она хочет быть выебанной видя матан
|
||||||
|
// Покайфу когда ты Виет а вокруг лишь дискриминант
|
||||||
case 'status':
|
case 'status':
|
||||||
if($usr->getStatus() != null)
|
if($usr->getStatus() != null)
|
||||||
$response[$i]->status = $usr->getStatus();
|
$response[$i]->status = $usr->getStatus();
|
||||||
|
@ -71,10 +91,10 @@ final class Users extends VKAPIRequestHandler
|
||||||
$response[$i]->screen_name = $usr->getShortCode();
|
$response[$i]->screen_name = $usr->getShortCode();
|
||||||
break;
|
break;
|
||||||
case 'friend_status':
|
case 'friend_status':
|
||||||
switch($usr->getSubscriptionStatus($this->getUser())) {
|
switch($usr->getSubscriptionStatus($authuser)) {
|
||||||
case 3:
|
case 3:
|
||||||
case 0:
|
case 0:
|
||||||
$response[$i]->friend_status = $usr->getSubscriptionStatus($this->getUser());
|
$response[$i]->friend_status = $usr->getSubscriptionStatus($authuser);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
$response[$i]->friend_status = 2;
|
$response[$i]->friend_status = 2;
|
||||||
|
@ -158,13 +178,14 @@ final class Users extends VKAPIRequestHandler
|
||||||
$users = new UsersRepo;
|
$users = new UsersRepo;
|
||||||
|
|
||||||
$array = [];
|
$array = [];
|
||||||
|
$find = $users->find($q);
|
||||||
|
|
||||||
foreach ($users->find($q) as $user) {
|
foreach ($find as $user) {
|
||||||
$array[] = $user->getId();
|
$array[] = $user->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (object)[
|
return (object)[
|
||||||
"count" => $users->getFoundCount($q),
|
"count" => $find->size(),
|
||||||
"items" => $this->get(implode(',', $array), $fields, $offset, $count)
|
"items" => $this->get(implode(',', $array), $fields, $offset, $count)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,27 @@ final class Wall extends VKAPIRequestHandler
|
||||||
|
|
||||||
foreach ($posts->getPostsFromUsersWall((int)$owner_id, 1, $count, $offset) as $post) {
|
foreach ($posts->getPostsFromUsersWall((int)$owner_id, 1, $count, $offset) as $post) {
|
||||||
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
|
|
||||||
|
$attachments;
|
||||||
|
foreach($post->getChildren() as $attachment)
|
||||||
|
{
|
||||||
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||||
|
{
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "photo",
|
||||||
|
"photo" => [
|
||||||
|
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
|
||||||
|
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||||
|
"id" => $attachment->getVirtualId(),
|
||||||
|
"owner_id" => $attachment->getOwner()->getId(),
|
||||||
|
"sizes" => array_values($attachment->getVkApiSizes()),
|
||||||
|
"text" => "",
|
||||||
|
"has_tags" => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$items[] = (object)[
|
$items[] = (object)[
|
||||||
"id" => $post->getVirtualId(),
|
"id" => $post->getVirtualId(),
|
||||||
"from_id" => $from_id,
|
"from_id" => $from_id,
|
||||||
|
@ -35,6 +56,7 @@ final class Wall extends VKAPIRequestHandler
|
||||||
"can_archive" => false, // TODO MAYBE
|
"can_archive" => false, // TODO MAYBE
|
||||||
"is_archived" => false,
|
"is_archived" => false,
|
||||||
"is_pinned" => $post->isPinned(),
|
"is_pinned" => $post->isPinned(),
|
||||||
|
"attachments" => $attachments,
|
||||||
"post_source" => (object)["type" => "vk"],
|
"post_source" => (object)["type" => "vk"],
|
||||||
"comments" => (object)[
|
"comments" => (object)[
|
||||||
"count" => $post->getCommentsCount(),
|
"count" => $post->getCommentsCount(),
|
||||||
|
@ -56,6 +78,8 @@ final class Wall extends VKAPIRequestHandler
|
||||||
$profiles[] = $from_id;
|
$profiles[] = $from_id;
|
||||||
else
|
else
|
||||||
$groups[] = $from_id * -1;
|
$groups[] = $from_id * -1;
|
||||||
|
|
||||||
|
$attachments = null; // free attachments so it will not clone everythingg
|
||||||
}
|
}
|
||||||
|
|
||||||
if($extended == 1)
|
if($extended == 1)
|
||||||
|
@ -110,6 +134,169 @@ final class Wall extends VKAPIRequestHandler
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getById(string $posts, int $extended = 0, string $fields = "", User $user = null)
|
||||||
|
{
|
||||||
|
if($user == null) $user = $this->getUser(); // костыли костыли крылышки
|
||||||
|
|
||||||
|
$items = [];
|
||||||
|
$profiles = [];
|
||||||
|
$groups = [];
|
||||||
|
# $count = $posts->getPostCountOnUserWall((int) $owner_id);
|
||||||
|
|
||||||
|
$psts = explode(",", $posts);
|
||||||
|
|
||||||
|
foreach($psts as $pst)
|
||||||
|
{
|
||||||
|
$id = explode("_", $pst);
|
||||||
|
$post = (new PostsRepo)->getPostById(intval($id[0]), intval($id[1]));
|
||||||
|
if($post) {
|
||||||
|
$from_id = get_class($post->getOwner()) == "openvk\Web\Models\Entities\Club" ? $post->getOwner()->getId() * (-1) : $post->getOwner()->getId();
|
||||||
|
$attachments;
|
||||||
|
foreach($post->getChildren() as $attachment)
|
||||||
|
{
|
||||||
|
if($attachment instanceof \openvk\Web\Models\Entities\Photo)
|
||||||
|
{
|
||||||
|
$attachments[] = [
|
||||||
|
"type" => "photo",
|
||||||
|
"photo" => [
|
||||||
|
"album_id" => $attachment->getAlbum() ? $attachment->getAlbum()->getId() : null,
|
||||||
|
"date" => $attachment->getPublicationTime()->timestamp(),
|
||||||
|
"id" => $attachment->getVirtualId(),
|
||||||
|
"owner_id" => $attachment->getOwner()->getId(),
|
||||||
|
"sizes" => array(
|
||||||
|
[
|
||||||
|
"height" => 2560,
|
||||||
|
"url" => $attachment->getURLBySizeId("normal"),
|
||||||
|
"type" => "m",
|
||||||
|
"width" => 2560,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"height" => 130,
|
||||||
|
"url" => $attachment->getURLBySizeId("tiny"),
|
||||||
|
"type" => "o",
|
||||||
|
"width" => 130,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"height" => 604,
|
||||||
|
"url" => $attachment->getURLBySizeId("normal"),
|
||||||
|
"type" => "p",
|
||||||
|
"width" => 604,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"height" => 807,
|
||||||
|
"url" => $attachment->getURLBySizeId("large"),
|
||||||
|
"type" => "q",
|
||||||
|
"width" => 807,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"height" => 1280,
|
||||||
|
"url" => $attachment->getURLBySizeId("larger"),
|
||||||
|
"type" => "r",
|
||||||
|
"width" => 1280,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"height" => 75, // Для временного компросима оставляю статическое число. Если каждый раз обращаться к файлу за количеством пикселов, то наступает пuпuська полная с производительностью, так что пока так
|
||||||
|
"url" => $attachment->getURLBySizeId("miniscule"),
|
||||||
|
"type" => "s",
|
||||||
|
"width" => 75,
|
||||||
|
]),
|
||||||
|
"text" => "",
|
||||||
|
"has_tags" => false
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$items[] = (object)[
|
||||||
|
"id" => $post->getVirtualId(),
|
||||||
|
"from_id" => $from_id,
|
||||||
|
"owner_id" => $post->getTargetWall(),
|
||||||
|
"date" => $post->getPublicationTime()->timestamp(),
|
||||||
|
"post_type" => "post",
|
||||||
|
"text" => $post->getText(),
|
||||||
|
"can_edit" => 0, // TODO
|
||||||
|
"can_delete" => $post->canBeDeletedBy($user),
|
||||||
|
"can_pin" => $post->canBePinnedBy($user),
|
||||||
|
"can_archive" => false, // TODO MAYBE
|
||||||
|
"is_archived" => false,
|
||||||
|
"is_pinned" => $post->isPinned(),
|
||||||
|
"post_source" => (object)["type" => "vk"],
|
||||||
|
"attachments" => $attachments,
|
||||||
|
"comments" => (object)[
|
||||||
|
"count" => $post->getCommentsCount(),
|
||||||
|
"can_post" => 1
|
||||||
|
],
|
||||||
|
"likes" => (object)[
|
||||||
|
"count" => $post->getLikesCount(),
|
||||||
|
"user_likes" => (int) $post->hasLikeFrom($user),
|
||||||
|
"can_like" => 1,
|
||||||
|
"can_publish" => 1,
|
||||||
|
],
|
||||||
|
"reposts" => (object)[
|
||||||
|
"count" => $post->getRepostCount(),
|
||||||
|
"user_reposted" => 0
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($from_id > 0)
|
||||||
|
$profiles[] = $from_id;
|
||||||
|
else
|
||||||
|
$groups[] = $from_id * -1;
|
||||||
|
|
||||||
|
$attachments = null; // free attachments so it will not clone everythingg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($extended == 1)
|
||||||
|
{
|
||||||
|
$profiles = array_unique($profiles);
|
||||||
|
$groups = array_unique($groups);
|
||||||
|
|
||||||
|
$profilesFormatted = [];
|
||||||
|
$groupsFormatted = [];
|
||||||
|
|
||||||
|
foreach ($profiles as $prof) {
|
||||||
|
$user = (new UsersRepo)->get($prof);
|
||||||
|
$profilesFormatted[] = (object)[
|
||||||
|
"first_name" => $user->getFirstName(),
|
||||||
|
"id" => $user->getId(),
|
||||||
|
"last_name" => $user->getLastName(),
|
||||||
|
"can_access_closed" => false,
|
||||||
|
"is_closed" => false,
|
||||||
|
"sex" => $user->isFemale() ? 1 : 2,
|
||||||
|
"screen_name" => $user->getShortCode(),
|
||||||
|
"photo_50" => $user->getAvatarUrl(),
|
||||||
|
"photo_100" => $user->getAvatarUrl(),
|
||||||
|
"online" => $user->isOnline()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($groups as $g) {
|
||||||
|
$group = (new ClubsRepo)->get($g);
|
||||||
|
$groupsFormatted[] = (object)[
|
||||||
|
"id" => $group->getId(),
|
||||||
|
"name" => $group->getName(),
|
||||||
|
"screen_name" => $group->getShortCode(),
|
||||||
|
"is_closed" => 0,
|
||||||
|
"type" => "group",
|
||||||
|
"photo_50" => $group->getAvatarUrl(),
|
||||||
|
"photo_100" => $group->getAvatarUrl(),
|
||||||
|
"photo_200" => $group->getAvatarUrl(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return (object)[
|
||||||
|
"items" => (array)$items,
|
||||||
|
"profiles" => (array)$profilesFormatted,
|
||||||
|
"groups" => (array)$groupsFormatted
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return (object)[
|
||||||
|
"items" => (array)$items
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0): object
|
function post(string $owner_id, string $message = "", int $from_group = 0, int $signed = 0): object
|
||||||
{
|
{
|
||||||
$this->requireUser();
|
$this->requireUser();
|
||||||
|
|
|
@ -11,10 +11,11 @@ final class Message
|
||||||
public $out;
|
public $out;
|
||||||
public $title = "";
|
public $title = "";
|
||||||
public $body;
|
public $body;
|
||||||
|
public $text;
|
||||||
public $attachments = [];
|
public $attachments = [];
|
||||||
public $fwd_messages = [];
|
public $fwd_messages = [];
|
||||||
public $emoji;
|
public $emoji;
|
||||||
public $important = 1;
|
public $important = true;
|
||||||
public $deleted = 0;
|
public $deleted = 0;
|
||||||
public $random_id = NULL;
|
public $random_id = NULL;
|
||||||
}
|
}
|
||||||
|
|
16
Vagrantfile
vendored
16
Vagrantfile
vendored
|
@ -1,16 +1,22 @@
|
||||||
# -*- mode: ruby -*-
|
# -*- mode: ruby -*-
|
||||||
# vi: set ft=ruby :
|
# vi: set ft=ruby :
|
||||||
Vagrant.configure("2") do |config|
|
Vagrant.configure("2") do |config|
|
||||||
config.vm.box = "freebsd/FreeBSD-12.1-STABLE"
|
config.vm.box = "freebsd/FreeBSD-13.1-RC2"
|
||||||
|
config.vm.box_version = "2022.04.07"
|
||||||
|
|
||||||
config.vm.network "forwarded_port", guest: 80, host: 4000
|
config.vm.network "forwarded_port", guest: 80, host: 4000
|
||||||
|
|
||||||
config.vm.synced_folder ".", "/.ovk_release"
|
|
||||||
|
|
||||||
config.vm.provider "virtualbox" do |vb|
|
config.vm.provider "virtualbox" do |vb|
|
||||||
vb.gui = true
|
vb.gui = true
|
||||||
vb.memory = "1024"
|
vb.cpus = 4
|
||||||
|
vb.memory = "1568"
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provider "vmware_workstation" do |vwx|
|
||||||
|
vwx.gui = true
|
||||||
|
vwx.vmx["memsize"] = "1568"
|
||||||
|
vwx.vmx["numvcpus"] = "4"
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.provision "shell", inline: "/bin/tcsh /.ovk_release/install/automated/freebsd-12/install"
|
config.vm.provision "shell", inline: "/bin/tcsh /.ovk_release/install/automated/freebsd-13/install"
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,12 +38,12 @@ class Club extends RowModel
|
||||||
return iterator_to_array($avPhotos)[0] ?? NULL;
|
return iterator_to_array($avPhotos)[0] ?? NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAvatarUrl(): string
|
function getAvatarUrl(string $size = "miniscule"): string
|
||||||
{
|
{
|
||||||
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||||
$avPhoto = $this->getAvatarPhoto();
|
$avPhoto = $this->getAvatarPhoto();
|
||||||
|
|
||||||
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURL();
|
return is_null($avPhoto) ? "$serverUrl/assets/packages/static/openvk/img/camera_200.png" : $avPhoto->getURLBySizeId($size);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAvatarLink(): string
|
function getAvatarLink(): string
|
||||||
|
@ -346,6 +346,11 @@ class Club extends RowModel
|
||||||
{
|
{
|
||||||
return $this->getRecord()->website;
|
return $this->getRecord()->website;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAlert(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->alert;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TSubscribable;
|
use Traits\TSubscribable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,9 @@ abstract class Media extends Postable
|
||||||
{
|
{
|
||||||
protected $fileExtension = "oct"; #octet stream xddd
|
protected $fileExtension = "oct"; #octet stream xddd
|
||||||
protected $upperNodeReferenceColumnName = "owner";
|
protected $upperNodeReferenceColumnName = "owner";
|
||||||
|
protected $processingPlaceholder = NULL;
|
||||||
|
protected $processingTime = 30;
|
||||||
|
|
||||||
function __destruct()
|
function __destruct()
|
||||||
{
|
{
|
||||||
#Remove data, if model wasn't presisted
|
#Remove data, if model wasn't presisted
|
||||||
|
@ -22,6 +24,11 @@ abstract class Media extends Postable
|
||||||
else
|
else
|
||||||
return OPENVK_ROOT . "/storage/";
|
return OPENVK_ROOT . "/storage/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function checkIfFileIsProcessed(): bool
|
||||||
|
{
|
||||||
|
throw new \LogicException("checkIfFileIsProcessed is not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function saveFile(string $filename, string $hash): bool;
|
abstract protected function saveFile(string $filename, string $hash): bool;
|
||||||
|
|
||||||
|
@ -41,6 +48,10 @@ abstract class Media extends Postable
|
||||||
|
|
||||||
function getURL(): string
|
function getURL(): string
|
||||||
{
|
{
|
||||||
|
if(!is_null($this->processingPlaceholder))
|
||||||
|
if(!$this->isProcessed())
|
||||||
|
return "/assets/packages/static/openvk/$this->processingPlaceholder.$this->fileExtension";
|
||||||
|
|
||||||
$hash = $this->getRecord()->hash;
|
$hash = $this->getRecord()->hash;
|
||||||
|
|
||||||
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
|
switch(OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["mode"]) {
|
||||||
|
@ -55,7 +66,7 @@ abstract class Media extends Postable
|
||||||
case "server":
|
case "server":
|
||||||
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
|
$settings = (object) OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["server"];
|
||||||
return (
|
return (
|
||||||
$settings->protocol .
|
$settings->protocol ?? ovk_scheme() .
|
||||||
"://" . $settings->host .
|
"://" . $settings->host .
|
||||||
$settings->path .
|
$settings->path .
|
||||||
substr($hash, 0, 2) . "/$hash.$this->fileExtension"
|
substr($hash, 0, 2) . "/$hash.$this->fileExtension"
|
||||||
|
@ -68,6 +79,26 @@ abstract class Media extends Postable
|
||||||
{
|
{
|
||||||
return $this->getRecord()->description;
|
return $this->getRecord()->description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function isProcessed(): bool
|
||||||
|
{
|
||||||
|
if(is_null($this->processingPlaceholder))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if($this->getRecord()->processed)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
$timeDiff = time() - $this->getRecord()->last_checked;
|
||||||
|
if($timeDiff < $this->processingTime)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$res = $this->checkIfFileIsProcessed();
|
||||||
|
$this->stateChanges("last_checked", time());
|
||||||
|
$this->stateChanges("processed", $res);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
function isDeleted(): bool
|
function isDeleted(): bool
|
||||||
{
|
{
|
||||||
|
@ -89,7 +120,17 @@ abstract class Media extends Postable
|
||||||
|
|
||||||
$this->stateChanges("hash", $hash);
|
$this->stateChanges("hash", $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function save(): void
|
||||||
|
{
|
||||||
|
if(!is_null($this->processingPlaceholder) && is_null($this->getRecord())) {
|
||||||
|
$this->stateChanges("processed", 0);
|
||||||
|
$this->stateChanges("last_checked", time());
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
function delete(bool $softly = true): void
|
function delete(bool $softly = true): void
|
||||||
{
|
{
|
||||||
$deleteQuirk = ovkGetQuirk("blobs.erase-upon-deletion");
|
$deleteQuirk = ovkGetQuirk("blobs.erase-upon-deletion");
|
||||||
|
|
|
@ -48,6 +48,7 @@ class Note extends Postable
|
||||||
"acronym",
|
"acronym",
|
||||||
"blockquote",
|
"blockquote",
|
||||||
"cite",
|
"cite",
|
||||||
|
"span",
|
||||||
]);
|
]);
|
||||||
$config->set("HTML.AllowedAttributes", [
|
$config->set("HTML.AllowedAttributes", [
|
||||||
"table.summary",
|
"table.summary",
|
||||||
|
@ -59,6 +60,8 @@ class Note extends Postable
|
||||||
"img.style",
|
"img.style",
|
||||||
"div.style",
|
"div.style",
|
||||||
"div.title",
|
"div.title",
|
||||||
|
"span.class",
|
||||||
|
"p.class",
|
||||||
]);
|
]);
|
||||||
$config->set("CSS.AllowedProperties", [
|
$config->set("CSS.AllowedProperties", [
|
||||||
"float",
|
"float",
|
||||||
|
@ -68,6 +71,9 @@ class Note extends Postable
|
||||||
"max-width",
|
"max-width",
|
||||||
"font-weight",
|
"font-weight",
|
||||||
]);
|
]);
|
||||||
|
$config->set("Attr.AllowedClasses", [
|
||||||
|
"underline",
|
||||||
|
]);
|
||||||
|
|
||||||
$purifier = new HTMLPurifier($config);
|
$purifier = new HTMLPurifier($config);
|
||||||
return $purifier->purify($this->getRecord()->source);
|
return $purifier->purify($this->getRecord()->source);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use MessagePack\MessagePack;
|
||||||
|
use openvk\Web\Models\Entities\Album;
|
||||||
|
use openvk\Web\Models\Repositories\Albums;
|
||||||
use Chandler\Database\DatabaseConnection as DB;
|
use Chandler\Database\DatabaseConnection as DB;
|
||||||
use Nette\InvalidStateException as ISE;
|
use Nette\InvalidStateException as ISE;
|
||||||
use Nette\Utils\Image;
|
use Nette\Utils\Image;
|
||||||
|
@ -8,21 +11,83 @@ class Photo extends Media
|
||||||
{
|
{
|
||||||
protected $tableName = "photos";
|
protected $tableName = "photos";
|
||||||
protected $fileExtension = "jpeg";
|
protected $fileExtension = "jpeg";
|
||||||
|
|
||||||
const ALLOWED_SIDE_MULTIPLIER = 7;
|
const ALLOWED_SIDE_MULTIPLIER = 7;
|
||||||
|
|
||||||
|
private function resizeImage(string $filename, string $outputDir, \SimpleXMLElement $size): array
|
||||||
|
{
|
||||||
|
$res = [false];
|
||||||
|
$image = Image::fromFile($filename);
|
||||||
|
$requiresProportion = ((string) $size["requireProp"]) != "none";
|
||||||
|
if($requiresProportion) {
|
||||||
|
$props = explode(":", (string) $size["requireProp"]);
|
||||||
|
$px = (int) $props[0];
|
||||||
|
$py = (int) $props[1];
|
||||||
|
if(($image->getWidth() / $image->getHeight()) > ($px / $py)) {
|
||||||
|
# For some weird reason using resize with EXACT flag causes system to consume an unholy amount of RAM
|
||||||
|
$image->crop(0, 0, "100%", (int) ceil(($px * $image->getWidth()) / $py));
|
||||||
|
$res[0] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($size["maxSize"])) {
|
||||||
|
$maxSize = (int) $size["maxSize"];
|
||||||
|
$image->resize($maxSize, $maxSize, Image::SHRINK_ONLY | Image::FIT);
|
||||||
|
} else if(isset($size["maxResolution"])) {
|
||||||
|
$resolution = explode("x", (string) $size["maxResolution"]);
|
||||||
|
$image->resize((int) $resolution[0], (int) $resolution[1], Image::SHRINK_ONLY | Image::FIT);
|
||||||
|
} else {
|
||||||
|
throw new \RuntimeException("Malformed size description: " . (string) $size["id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$res[1] = $image->getWidth();
|
||||||
|
$res[2] = $image->getHeight();
|
||||||
|
if($res[1] <= 300 || $res[2] <= 300)
|
||||||
|
$image->save("$outputDir/" . (string) $size["id"] . ".gif");
|
||||||
|
else
|
||||||
|
$image->save("$outputDir/" . (string) $size["id"] . ".jpeg");
|
||||||
|
|
||||||
|
imagedestroy($image->getImageResource());
|
||||||
|
unset($image);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveImageResizedCopies(string $filename, string $hash): void
|
||||||
|
{
|
||||||
|
$dir = dirname($this->pathFromHash($hash));
|
||||||
|
$dir = "$dir/$hash" . "_cropped";
|
||||||
|
if(!is_dir($dir)) {
|
||||||
|
@unlink($dir); # Added to transparently bypass issues with dead pesudofolders summoned by buggy SWIFT impls (selectel)
|
||||||
|
mkdir($dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sizes = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
|
||||||
|
if(!$sizes)
|
||||||
|
throw new \RuntimeException("Could not load photosizes.xml!");
|
||||||
|
|
||||||
|
$sizesMeta = [];
|
||||||
|
foreach($sizes->Size as $size)
|
||||||
|
$sizesMeta[(string) $size["id"]] = $this->resizeImage($filename, $dir, $size);
|
||||||
|
|
||||||
|
$sizesMeta = MessagePack::pack($sizesMeta);
|
||||||
|
$this->stateChanges("sizes", $sizesMeta);
|
||||||
|
}
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
$image = Image::fromFile($filename);
|
$image = Image::fromFile($filename);
|
||||||
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
if(($image->height >= ($image->width * Photo::ALLOWED_SIDE_MULTIPLIER)) || ($image->width >= ($image->height * Photo::ALLOWED_SIDE_MULTIPLIER)))
|
||||||
throw new ISE("Invalid layout: image is too wide/short");
|
throw new ISE("Invalid layout: image is too wide/short");
|
||||||
|
|
||||||
|
$image->resize(8192, 4320, Image::SHRINK_ONLY | Image::FIT);
|
||||||
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
$image->save($this->pathFromHash($hash), 92, Image::JPEG);
|
||||||
|
$this->saveImageResizedCopies($filename, $hash);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function crop(real $left, real $top, real $width, real $height): bool
|
function crop(real $left, real $top, real $width, real $height): void
|
||||||
{
|
{
|
||||||
if(isset($this->changes["hash"]))
|
if(isset($this->changes["hash"]))
|
||||||
$hash = $this->changes["hash"];
|
$hash = $this->changes["hash"];
|
||||||
|
@ -33,7 +98,7 @@ class Photo extends Media
|
||||||
|
|
||||||
$image = Image::fromFile($this->pathFromHash($hash));
|
$image = Image::fromFile($this->pathFromHash($hash));
|
||||||
$image->crop($left, $top, $width, $height);
|
$image->crop($left, $top, $width, $height);
|
||||||
return $image->save($this->pathFromHash($hash));
|
$image->save($this->pathFromHash($hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isolate(): void
|
function isolate(): void
|
||||||
|
@ -43,7 +108,131 @@ class Photo extends Media
|
||||||
|
|
||||||
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
DB::i()->getContext()->table("album_relations")->where("media", $this->getRecord()->id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSizes(bool $upgrade = false, bool $forceUpdate = false): ?array
|
||||||
|
{
|
||||||
|
$sizes = $this->getRecord()->sizes;
|
||||||
|
if(!$sizes || $forceUpdate) {
|
||||||
|
if($forceUpdate || $upgrade || OPENVK_ROOT_CONF["openvk"]["preferences"]["photos"]["upgradeStructure"]) {
|
||||||
|
$hash = $this->getRecord()->hash;
|
||||||
|
$this->saveImageResizedCopies($this->pathFromHash($hash), $hash);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $this->getSizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = [];
|
||||||
|
$sizes = MessagePack::unpack($sizes);
|
||||||
|
foreach($sizes as $id => $meta) {
|
||||||
|
$url = $this->getURL();
|
||||||
|
$url = str_replace(".$this->fileExtension", "_cropped/$id.", $url);
|
||||||
|
$url .= ($meta[1] <= 300 || $meta[2] <= 300) ? "gif" : "jpeg";
|
||||||
|
|
||||||
|
$res[$id] = (object) [
|
||||||
|
"url" => $url,
|
||||||
|
"width" => $meta[1],
|
||||||
|
"height" => $meta[2],
|
||||||
|
"crop" => $meta[0]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
[$x, $y] = $this->getDimensions();
|
||||||
|
$res["UPLOADED_MAXRES"] = (object) [
|
||||||
|
"url" => $this->getURL(),
|
||||||
|
"width" => $x,
|
||||||
|
"height" => $y,
|
||||||
|
"crop" => false
|
||||||
|
];
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVkApiSizes(): ?array
|
||||||
|
{
|
||||||
|
$res = [];
|
||||||
|
$sizes = $this->getSizes();
|
||||||
|
if(!$sizes)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$manifest = simplexml_load_file(OPENVK_ROOT . "/data/photosizes.xml");
|
||||||
|
if(!$manifest)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
$mappings = [];
|
||||||
|
foreach($manifest->Size as $size)
|
||||||
|
$mappings[(string) $size["id"]] = (string) $size["vkId"];
|
||||||
|
|
||||||
|
foreach($sizes as $id => $meta) {
|
||||||
|
$type = $mappings[$id] ?? $id;
|
||||||
|
$meta->type = $type;
|
||||||
|
$res[$type] = $meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getURLBySizeId(string $size): string
|
||||||
|
{
|
||||||
|
$sizes = $this->getSizes();
|
||||||
|
if(!$sizes)
|
||||||
|
return $this->getURL();
|
||||||
|
|
||||||
|
$size = $sizes[$size];
|
||||||
|
if(!$size)
|
||||||
|
return $this->getURL();
|
||||||
|
|
||||||
|
return $size->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDimensions(): array
|
||||||
|
{
|
||||||
|
$x = $this->getRecord()->width;
|
||||||
|
$y = $this->getRecord()->height;
|
||||||
|
if(!$x) { # no sizes in database
|
||||||
|
$hash = $this->getRecord()->hash;
|
||||||
|
$image = Image::fromFile($this->pathFromHash($hash));
|
||||||
|
|
||||||
|
$x = $image->getWidth();
|
||||||
|
$y = $image->getHeight();
|
||||||
|
$this->stateChanges("width", $x);
|
||||||
|
$this->stateChanges("height", $y);
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$x, $y];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAlbum(): ?Album
|
||||||
|
{
|
||||||
|
return (new Albums)->getAlbumByPhotoId($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toVkApiStruct(): object
|
||||||
|
{
|
||||||
|
$res = (object) [];
|
||||||
|
|
||||||
|
$res->id = $res->pid = $this->getId();
|
||||||
|
$res->owner_id = $res->user_id = $this->getOwner()->getId()->getId();
|
||||||
|
$res->aid = $res->album_id = NULL;
|
||||||
|
$res->width = $this->getDimensions()[0];
|
||||||
|
$res->height = $this->getDimensions()[1];
|
||||||
|
$res->date = $res->created = $this->getPublicationTime()->timestamp();
|
||||||
|
|
||||||
|
$res->sizes = $this->getVkApiSizes();
|
||||||
|
$res->src_small = $res->photo_75 = $this->getURLBySizeId("miniscule");
|
||||||
|
$res->src = $res->photo_130 = $this->getURLBySizeId("tiny");
|
||||||
|
$res->src_big = $res->photo_604 = $this->getURLBySizeId("normal");
|
||||||
|
$res->src_xbig = $res->photo_807 = $this->getURLBySizeId("large");
|
||||||
|
$res->src_xxbig = $res->photo_1280 = $this->getURLBySizeId("larger");
|
||||||
|
$res->src_xxxbig = $res->photo_2560 = $this->getURLBySizeId("original");
|
||||||
|
$res->src_original = $res->url = $this->getURLBySizeId("UPLOADED_MAXRES");
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
static function fastMake(int $owner, string $description = "", array $file, ?Album $album = NULL, bool $anon = false): Photo
|
||||||
{
|
{
|
||||||
$photo = new static;
|
$photo = new static;
|
||||||
|
@ -53,10 +242,10 @@ class Photo extends Media
|
||||||
$photo->setCreated(time());
|
$photo->setCreated(time());
|
||||||
$photo->setFile($file);
|
$photo->setFile($file);
|
||||||
$photo->save();
|
$photo->save();
|
||||||
|
|
||||||
if(!is_null($album))
|
if(!is_null($album))
|
||||||
$album->addPhoto($photo);
|
$album->addPhoto($photo);
|
||||||
|
|
||||||
return $photo;
|
return $photo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,16 +55,21 @@ trait TRichText
|
||||||
{
|
{
|
||||||
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
$contentColumn = property_exists($this, "overrideContentColumn") ? $this->overrideContentColumn : "content";
|
||||||
|
|
||||||
$text = htmlentities($this->getRecord()->{$contentColumn}, ENT_DISALLOWED | ENT_XHTML);
|
$text = htmlspecialchars($this->getRecord()->{$contentColumn}, ENT_DISALLOWED | ENT_XHTML);
|
||||||
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
$proc = iconv_strlen($this->getRecord()->{$contentColumn}) <= OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["postSizes"]["processingLimit"];
|
||||||
if($html) {
|
if($html) {
|
||||||
if($proc) {
|
if($proc) {
|
||||||
$rel = $this->isAd() ? "sponsored" : "ugc";
|
$rel = $this->isAd() ? "sponsored" : "ugc";
|
||||||
$text = $this->formatLinks($text);
|
$text = $this->formatLinks($text);
|
||||||
$text = preg_replace("%@([A-Za-z0-9]++) \(([\p{L} 0-9]+)\)%Xu", "[$1|$2]", $text);
|
$text = preg_replace("%@([A-Za-z0-9]++) \(((?:[\p{L&}\p{Lo} 0-9]\p{Mn}?)++)\)%Xu", "[$1|$2]", $text);
|
||||||
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
$text = preg_replace("%([\n\r\s]|^)(@([A-Za-z0-9]++))%Xu", "$1[$3|@$3]", $text);
|
||||||
$text = preg_replace("%\[([A-Za-z0-9]++)\|([\p{L} 0-9@]+)\]%Xu", "<a href='/$1'>$2</a>", $text);
|
$text = preg_replace("%\[([A-Za-z0-9]++)\|((?:[\p{L&}\p{Lo} 0-9@]\p{Mn}?)++)\]%Xu", "<a href='/$1'>$2</a>", $text);
|
||||||
$text = preg_replace("%([\n\r\s]|^)(#([\p{L}_-]++[0-9]*[\p{L}_-]*))%Xu", "$1<a href='/feed/hashtag/$3'>$2</a>", $text);
|
$text = preg_replace_callback("%([\n\r\s]|^)(\#([\p{L}_0-9][\p{L}_0-9\(\)\-\']+[\p{L}_0-9\(\)]|[\p{L}_0-9]{1,2}))%Xu", function($m) {
|
||||||
|
$slug = rawurlencode($m[3]);
|
||||||
|
|
||||||
|
return "$m[1]<a href='/feed/hashtag/$slug'>$m[2]</a>";
|
||||||
|
}, $text);
|
||||||
|
|
||||||
$text = $this->formatEmojis($text);
|
$text = $this->formatEmojis($text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Entities;
|
namespace openvk\Web\Models\Entities;
|
||||||
|
use morphos\Gender;
|
||||||
use openvk\Web\Themes\{Themepack, Themepacks};
|
use openvk\Web\Themes\{Themepack, Themepacks};
|
||||||
use openvk\Web\Util\DateTime;
|
use openvk\Web\Util\DateTime;
|
||||||
use openvk\Web\Models\RowModel;
|
use openvk\Web\Models\RowModel;
|
||||||
|
@ -9,6 +10,7 @@ use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
use Chandler\Database\DatabaseConnection;
|
use Chandler\Database\DatabaseConnection;
|
||||||
use Chandler\Security\User as ChandlerUser;
|
use Chandler\Security\User as ChandlerUser;
|
||||||
|
use function morphos\Russian\inflectName;
|
||||||
|
|
||||||
class User extends RowModel
|
class User extends RowModel
|
||||||
{
|
{
|
||||||
|
@ -31,11 +33,11 @@ class User extends RowModel
|
||||||
const NSFW_TOLERANT = 1;
|
const NSFW_TOLERANT = 1;
|
||||||
const NSFW_FULL_TOLERANT = 2;
|
const NSFW_FULL_TOLERANT = 2;
|
||||||
|
|
||||||
protected function _abstractRelationGenerator(string $filename, int $page = 1): \Traversable
|
protected function _abstractRelationGenerator(string $filename, int $page = 1, int $limit = 6): \Traversable
|
||||||
{
|
{
|
||||||
$id = $this->getId();
|
$id = $this->getId();
|
||||||
$query = "SELECT id FROM\n" . file_get_contents(__DIR__ . "/../sql/$filename.tsql");
|
$query = "SELECT id FROM\n" . file_get_contents(__DIR__ . "/../sql/$filename.tsql");
|
||||||
$query .= "\n LIMIT 6 OFFSET " . ( ($page - 1) * 6 );
|
$query .= "\n LIMIT " . $limit . " OFFSET " . ( ($page - 1) * $limit );
|
||||||
|
|
||||||
$rels = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
$rels = DatabaseConnection::i()->getConnection()->query($query, $id, $id);
|
||||||
foreach($rels as $rel) {
|
foreach($rels as $rel) {
|
||||||
|
@ -102,7 +104,7 @@ class User extends RowModel
|
||||||
return "/id" . $this->getId();
|
return "/id" . $this->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAvatarUrl(): string
|
function getAvatarUrl(string $size = "miniscule"): string
|
||||||
{
|
{
|
||||||
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
$serverUrl = ovk_scheme(true) . $_SERVER["HTTP_HOST"];
|
||||||
|
|
||||||
|
@ -115,7 +117,7 @@ class User extends RowModel
|
||||||
if(is_null($avPhoto))
|
if(is_null($avPhoto))
|
||||||
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
return "$serverUrl/assets/packages/static/openvk/img/camera_200.png";
|
||||||
else
|
else
|
||||||
return $avPhoto->getURL();
|
return $avPhoto->getURLBySizeId($size);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAvatarLink(): string
|
function getAvatarLink(): string
|
||||||
|
@ -166,153 +168,169 @@ class User extends RowModel
|
||||||
|
|
||||||
return $this->getFirstName() . $pseudo . $this->getLastName();
|
return $this->getFirstName() . $pseudo . $this->getLastName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMorphedName(string $case = "genitive", bool $fullName = true): string
|
||||||
|
{
|
||||||
|
$name = $fullName ? ($this->getLastName() . " " . $this->getFirstName()) : $this->getFirstName();
|
||||||
|
if(!preg_match("%^[А-яё\-]+$%", $name))
|
||||||
|
return $name; # name is probably not russian
|
||||||
|
|
||||||
|
$inflected = inflectName($name, $case, $this->isFemale() ? Gender::FEMALE : Gender::MALE);
|
||||||
|
|
||||||
|
return $inflected ?: $name;
|
||||||
|
}
|
||||||
|
|
||||||
function getCanonicalName(): string
|
function getCanonicalName(): string
|
||||||
{
|
{
|
||||||
if($this->getRecord()->deleted)
|
if($this->getRecord()->deleted)
|
||||||
return "DELETED";
|
return "DELETED";
|
||||||
else
|
else
|
||||||
return $this->getFirstName() . ' ' . $this->getLastName();
|
return $this->getFirstName() . " " . $this->getLastName();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPhone(): ?string
|
function getPhone(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->phone;
|
return $this->getRecord()->phone;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEmail(): ?string
|
function getEmail(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->email;
|
return $this->getRecord()->email;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOnline(): DateTime
|
function getOnline(): DateTime
|
||||||
{
|
{
|
||||||
return new DateTime($this->getRecord()->online);
|
return new DateTime($this->getRecord()->online);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDescription(): ?string
|
function getDescription(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->about;
|
return $this->getRecord()->about;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStatus(): ?string
|
function getStatus(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->status;
|
return $this->getRecord()->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getShortCode(): ?string
|
function getShortCode(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->shortcode;
|
return $this->getRecord()->shortcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlert(): ?string
|
function getAlert(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->alert;
|
return $this->getRecord()->alert;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBanReason(): ?string
|
function getBanReason(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->block_reason;
|
return $this->getRecord()->block_reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBanInSupportReason(): ?string
|
||||||
|
{
|
||||||
|
return $this->getRecord()->block_in_support_reason;
|
||||||
|
}
|
||||||
|
|
||||||
function getType(): int
|
function getType(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->type;
|
return $this->getRecord()->type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCoins(): float
|
function getCoins(): float
|
||||||
{
|
{
|
||||||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
return $this->getRecord()->coins;
|
return $this->getRecord()->coins;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRating(): int
|
function getRating(): int
|
||||||
{
|
{
|
||||||
return OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"] ? $this->getRecord()->rating : 0;
|
return OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"] ? $this->getRecord()->rating : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReputation(): int
|
function getReputation(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->reputation;
|
return $this->getRecord()->reputation;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRegistrationTime(): DateTime
|
function getRegistrationTime(): DateTime
|
||||||
{
|
{
|
||||||
return new DateTime($this->getRecord()->since->getTimestamp());
|
return new DateTime($this->getRecord()->since->getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRegistrationIP(): string
|
function getRegistrationIP(): string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->registering_ip;
|
return $this->getRecord()->registering_ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHometown(): ?string
|
function getHometown(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->hometown;
|
return $this->getRecord()->hometown;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPoliticalViews(): int
|
function getPoliticalViews(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->polit_views;
|
return $this->getRecord()->polit_views;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMaritalStatus(): int
|
function getMaritalStatus(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->marital_status;
|
return $this->getRecord()->marital_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getContactEmail(): ?string
|
function getContactEmail(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->email_contact;
|
return $this->getRecord()->email_contact;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTelegram(): ?string
|
function getTelegram(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->telegram;
|
return $this->getRecord()->telegram;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInterests(): ?string
|
function getInterests(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->interests;
|
return $this->getRecord()->interests;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFavoriteMusic(): ?string
|
function getFavoriteMusic(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->fav_music;
|
return $this->getRecord()->fav_music;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFavoriteFilms(): ?string
|
function getFavoriteFilms(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->fav_films;
|
return $this->getRecord()->fav_films;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFavoriteShows(): ?string
|
function getFavoriteShows(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->fav_shows;
|
return $this->getRecord()->fav_shows;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFavoriteBooks(): ?string
|
function getFavoriteBooks(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->fav_books;
|
return $this->getRecord()->fav_books;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFavoriteQuote(): ?string
|
function getFavoriteQuote(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->fav_quote;
|
return $this->getRecord()->fav_quote;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCity(): ?string
|
function getCity(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->city;
|
return $this->getRecord()->city;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPhysicalAddress(): ?string
|
function getPhysicalAddress(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->address;
|
return $this->getRecord()->address;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotificationOffset(): int
|
function getNotificationOffset(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->notification_offset;
|
return $this->getRecord()->notification_offset;
|
||||||
|
@ -327,7 +345,7 @@ class User extends RowModel
|
||||||
{
|
{
|
||||||
return (int)floor((time() - $this->getBirthday()->timestamp()) / YEAR);
|
return (int)floor((time() - $this->getBirthday()->timestamp()) / YEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get2faSecret(): ?string
|
function get2faSecret(): ?string
|
||||||
{
|
{
|
||||||
return $this->getRecord()["2fa_secret"];
|
return $this->getRecord()["2fa_secret"];
|
||||||
|
@ -342,7 +360,7 @@ class User extends RowModel
|
||||||
{
|
{
|
||||||
$this->stateChanges("notification_offset", time());
|
$this->stateChanges("notification_offset", time());
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLeftMenuItemStatus(string $id): bool
|
function getLeftMenuItemStatus(string $id): bool
|
||||||
{
|
{
|
||||||
return (bool) bmask($this->getRecord()->left_menu, [
|
return (bool) bmask($this->getRecord()->left_menu, [
|
||||||
|
@ -359,7 +377,7 @@ class User extends RowModel
|
||||||
],
|
],
|
||||||
])->get($id);
|
])->get($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrivacySetting(string $id): int
|
function getPrivacySetting(string $id): int
|
||||||
{
|
{
|
||||||
return (int) bmask($this->getRecord()->privacy, [
|
return (int) bmask($this->getRecord()->privacy, [
|
||||||
|
@ -378,7 +396,7 @@ class User extends RowModel
|
||||||
],
|
],
|
||||||
])->get($id);
|
])->get($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrivacyPermission(string $permission, ?User $user = NULL): bool
|
function getPrivacyPermission(string $permission, ?User $user = NULL): bool
|
||||||
{
|
{
|
||||||
$permStatus = $this->getPrivacySetting($permission);
|
$permStatus = $this->getPrivacySetting($permission);
|
||||||
|
@ -386,7 +404,7 @@ class User extends RowModel
|
||||||
return $permStatus === User::PRIVACY_EVERYONE;
|
return $permStatus === User::PRIVACY_EVERYONE;
|
||||||
else if($user->getId() === $this->getId())
|
else if($user->getId() === $this->getId())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
switch($permStatus) {
|
switch($permStatus) {
|
||||||
case User::PRIVACY_ONLY_FRIENDS:
|
case User::PRIVACY_ONLY_FRIENDS:
|
||||||
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
return $this->getSubscriptionStatus($user) === User::SUBSCRIPTION_MUTUAL;
|
||||||
|
@ -397,12 +415,12 @@ class User extends RowModel
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProfileCompletenessReport(): object
|
function getProfileCompletenessReport(): object
|
||||||
{
|
{
|
||||||
$incompleteness = 0;
|
$incompleteness = 0;
|
||||||
$unfilled = [];
|
$unfilled = [];
|
||||||
|
|
||||||
if(!$this->getRecord()->status) {
|
if(!$this->getRecord()->status) {
|
||||||
$unfilled[] = "status";
|
$unfilled[] = "status";
|
||||||
$incompleteness += 15;
|
$incompleteness += 15;
|
||||||
|
@ -423,46 +441,46 @@ class User extends RowModel
|
||||||
$unfilled[] = "interests";
|
$unfilled[] = "interests";
|
||||||
$incompleteness += 20;
|
$incompleteness += 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
$total = max(100 - $incompleteness + $this->getRating(), 0);
|
$total = max(100 - $incompleteness + $this->getRating(), 0);
|
||||||
if(ovkGetQuirk("profile.rating-bar-behaviour") === 0)
|
if(ovkGetQuirk("profile.rating-bar-behaviour") === 0)
|
||||||
if ($total >= 100)
|
if ($total >= 100)
|
||||||
$percent = round(($total / 10**strlen(strval($total))) * 100, 0);
|
$percent = round(($total / 10**strlen(strval($total))) * 100, 0);
|
||||||
else
|
else
|
||||||
$percent = min($total, 100);
|
$percent = min($total, 100);
|
||||||
|
|
||||||
return (object) [
|
return (object) [
|
||||||
"total" => $total,
|
"total" => $total,
|
||||||
"percent" => $percent,
|
"percent" => $percent,
|
||||||
"unfilled" => $unfilled,
|
"unfilled" => $unfilled,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFriends(int $page = 1): \Traversable
|
function getFriends(int $page = 1, int $limit = 6): \Traversable
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationGenerator("get-friends", $page);
|
return $this->_abstractRelationGenerator("get-friends", $page, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFriendsCount(): int
|
function getFriendsCount(): int
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationCount("get-friends");
|
return $this->_abstractRelationCount("get-friends");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFollowers(int $page = 1): \Traversable
|
function getFollowers(int $page = 1, int $limit = 6): \Traversable
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationGenerator("get-followers", $page);
|
return $this->_abstractRelationGenerator("get-followers", $page, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFollowersCount(): int
|
function getFollowersCount(): int
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationCount("get-followers");
|
return $this->_abstractRelationCount("get-followers");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubscriptions(int $page = 1): \Traversable
|
function getSubscriptions(int $page = 1, int $limit = 6): \Traversable
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationGenerator("get-subscriptions-user", $page);
|
return $this->_abstractRelationGenerator("get-subscriptions-user", $page, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubscriptionsCount(): int
|
function getSubscriptionsCount(): int
|
||||||
{
|
{
|
||||||
return $this->_abstractRelationCount("get-subscriptions-user");
|
return $this->_abstractRelationCount("get-subscriptions-user");
|
||||||
|
@ -472,7 +490,7 @@ class User extends RowModel
|
||||||
{
|
{
|
||||||
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
return sizeof(DatabaseConnection::i()->getContext()->table("messages")->where(["recipient_id" => $this->getId(), "unread" => 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClubs(int $page = 1, bool $admin = false): \Traversable
|
function getClubs(int $page = 1, bool $admin = false): \Traversable
|
||||||
{
|
{
|
||||||
if($admin) {
|
if($admin) {
|
||||||
|
@ -497,7 +515,7 @@ class User extends RowModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getClubCount(bool $admin = false): int
|
function getClubCount(bool $admin = false): int
|
||||||
{
|
{
|
||||||
if($admin) {
|
if($admin) {
|
||||||
|
@ -512,7 +530,7 @@ class User extends RowModel
|
||||||
return sizeof($sel);
|
return sizeof($sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPinnedClubs(): \Traversable
|
function getPinnedClubs(): \Traversable
|
||||||
{
|
{
|
||||||
foreach($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true) as $target) {
|
foreach($this->getRecord()->related("groups.owner")->where("owner_club_pinned", true) as $target) {
|
||||||
|
@ -553,16 +571,16 @@ class User extends RowModel
|
||||||
foreach($sel as $target) {
|
foreach($sel as $target) {
|
||||||
$target = (new Clubs)->get($target->event);
|
$target = (new Clubs)->get($target->event);
|
||||||
if(!$target) continue;
|
if(!$target) continue;
|
||||||
|
|
||||||
yield $target;
|
yield $target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMeetingCount(): int
|
function getMeetingCount(): int
|
||||||
{
|
{
|
||||||
return sizeof($this->getRecord()->related("event_turnouts.user"));
|
return sizeof($this->getRecord()->related("event_turnouts.user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGifts(int $page = 1, ?int $perPage = NULL): \Traversable
|
function getGifts(int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
{
|
{
|
||||||
$gifts = $this->getRecord()->related("gift_user_relations.receiver")->order("sent DESC")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
$gifts = $this->getRecord()->related("gift_user_relations.receiver")->order("sent DESC")->page($page, $perPage ?? OPENVK_DEFAULT_PER_PAGE);
|
||||||
|
@ -576,7 +594,7 @@ class User extends RowModel
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGiftCount(): int
|
function getGiftCount(): int
|
||||||
{
|
{
|
||||||
return sizeof($this->getRecord()->related("gift_user_relations.receiver"));
|
return sizeof($this->getRecord()->related("gift_user_relations.receiver"));
|
||||||
|
@ -611,9 +629,9 @@ class User extends RowModel
|
||||||
|
|
||||||
function use2faBackupCode(int $code): bool
|
function use2faBackupCode(int $code): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
|
return (bool) $this->getRecord()->related("2fa_backup_codes.owner")->where("code", $code)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubscriptionStatus(User $user): int
|
function getSubscriptionStatus(User $user): int
|
||||||
{
|
{
|
||||||
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
|
$subbed = !is_null($this->getRecord()->related("subscriptions.follower")->where([
|
||||||
|
@ -624,71 +642,76 @@ class User extends RowModel
|
||||||
"model" => static::class,
|
"model" => static::class,
|
||||||
"follower" => $user->getId(),
|
"follower" => $user->getId(),
|
||||||
])->fetch());
|
])->fetch());
|
||||||
|
|
||||||
if($subbed && $followed) return User::SUBSCRIPTION_MUTUAL;
|
if($subbed && $followed) return User::SUBSCRIPTION_MUTUAL;
|
||||||
if($subbed) return User::SUBSCRIPTION_INCOMING;
|
if($subbed) return User::SUBSCRIPTION_INCOMING;
|
||||||
if($followed) return User::SUBSCRIPTION_OUTGOING;
|
if($followed) return User::SUBSCRIPTION_OUTGOING;
|
||||||
|
|
||||||
return User::SUBSCRIPTION_ABSENT;
|
return User::SUBSCRIPTION_ABSENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotificationsCount(bool $archived = false): int
|
function getNotificationsCount(bool $archived = false): int
|
||||||
{
|
{
|
||||||
return (new Notifications)->getNotificationCountByUser($this, $this->getNotificationOffset(), $archived);
|
return (new Notifications)->getNotificationCountByUser($this, $this->getNotificationOffset(), $archived);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNotifications(int $page, bool $archived = false): \Traversable
|
function getNotifications(int $page, bool $archived = false): \Traversable
|
||||||
{
|
{
|
||||||
return (new Notifications)->getNotificationsByUser($this, $this->getNotificationOffset(), $archived, $page);
|
return (new Notifications)->getNotificationsByUser($this, $this->getNotificationOffset(), $archived, $page);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPendingPhoneVerification(): ?ActiveRow
|
function getPendingPhoneVerification(): ?ActiveRow
|
||||||
{
|
{
|
||||||
return $this->getRecord()->ref("number_verification", "id");
|
return $this->getRecord()->ref("number_verification", "id");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRefLinkId(): string
|
function getRefLinkId(): string
|
||||||
{
|
{
|
||||||
$hash = hash_hmac("Snefru", (string) $this->getId(), CHANDLER_ROOT_CONF["security"]["secret"], true);
|
$hash = hash_hmac("Snefru", (string) $this->getId(), CHANDLER_ROOT_CONF["security"]["secret"], true);
|
||||||
|
|
||||||
return dechex($this->getId()) . " " . base64_encode($hash);
|
return dechex($this->getId()) . " " . base64_encode($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNsfwTolerance(): int
|
function getNsfwTolerance(): int
|
||||||
{
|
{
|
||||||
return $this->getRecord()->nsfw_tolerance;
|
return $this->getRecord()->nsfw_tolerance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isFemale(): bool
|
function isFemale(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->sex;
|
return (bool) $this->getRecord()->sex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVerified(): bool
|
function isVerified(): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->getRecord()->verified;
|
return (bool) $this->getRecord()->verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBanned(): bool
|
function isBanned(): bool
|
||||||
{
|
{
|
||||||
return !is_null($this->getBanReason());
|
return !is_null($this->getBanReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBannedInSupport(): bool
|
||||||
|
{
|
||||||
|
return !is_null($this->getBanInSupportReason());
|
||||||
|
}
|
||||||
|
|
||||||
function isOnline(): bool
|
function isOnline(): bool
|
||||||
{
|
{
|
||||||
return time() - $this->getRecord()->online <= 300;
|
return time() - $this->getRecord()->online <= 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prefersNotToSeeRating(): bool
|
function prefersNotToSeeRating(): bool
|
||||||
{
|
{
|
||||||
return !((bool) $this->getRecord()->show_rating);
|
return !((bool) $this->getRecord()->show_rating);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasPendingNumberChange(): bool
|
function hasPendingNumberChange(): bool
|
||||||
{
|
{
|
||||||
return !is_null($this->getPendingPhoneVerification());
|
return !is_null($this->getPendingPhoneVerification());
|
||||||
}
|
}
|
||||||
|
|
||||||
function gift(User $sender, Gift $gift, ?string $comment = NULL, bool $anonymous = false): void
|
function gift(User $sender, Gift $gift, ?string $comment = NULL, bool $anonymous = false): void
|
||||||
{
|
{
|
||||||
DatabaseConnection::i()->getContext()->table("gift_user_relations")->insert([
|
DatabaseConnection::i()->getContext()->table("gift_user_relations")->insert([
|
||||||
|
@ -700,7 +723,7 @@ class User extends RowModel
|
||||||
"sent" => time(),
|
"sent" => time(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ban(string $reason, bool $deleteSubscriptions = true): void
|
function ban(string $reason, bool $deleteSubscriptions = true): void
|
||||||
{
|
{
|
||||||
if($deleteSubscriptions) {
|
if($deleteSubscriptions) {
|
||||||
|
@ -713,42 +736,42 @@ class User extends RowModel
|
||||||
);
|
);
|
||||||
$subs->delete();
|
$subs->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setBlock_Reason($reason);
|
$this->setBlock_Reason($reason);
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyNumber(string $code): bool
|
function verifyNumber(string $code): bool
|
||||||
{
|
{
|
||||||
$ver = $this->getPendingPhoneVerification();
|
$ver = $this->getPendingPhoneVerification();
|
||||||
if(!$ver) return false;
|
if(!$ver) return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(sodium_memcmp((string) $ver->code, $code) === -1) return false;
|
if(sodium_memcmp((string) $ver->code, $code) === -1) return false;
|
||||||
} catch(\SodiumException $ex) {
|
} catch(\SodiumException $ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setPhone($ver->number);
|
$this->setPhone($ver->number);
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
DatabaseConnection::i()->getContext()
|
DatabaseConnection::i()->getContext()
|
||||||
->table("number_verification")
|
->table("number_verification")
|
||||||
->where("user", $this->getId())
|
->where("user", $this->getId())
|
||||||
->delete();
|
->delete();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFirst_Name(string $firstName): void
|
function setFirst_Name(string $firstName): void
|
||||||
{
|
{
|
||||||
$firstName = mb_convert_case($firstName, MB_CASE_TITLE);
|
$firstName = mb_convert_case($firstName, MB_CASE_TITLE);
|
||||||
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,16}$%u', $firstName))
|
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?(?:[\p{L&}\p{Lo}]\p{Mn}?){1,16}$%u', $firstName))
|
||||||
throw new InvalidUserNameException;
|
throw new InvalidUserNameException;
|
||||||
|
|
||||||
$this->stateChanges("first_name", $firstName);
|
$this->stateChanges("first_name", $firstName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLast_Name(string $lastName): void
|
function setLast_Name(string $lastName): void
|
||||||
{
|
{
|
||||||
if(!empty($lastName))
|
if(!empty($lastName))
|
||||||
|
@ -757,15 +780,15 @@ class User extends RowModel
|
||||||
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,16}(\-\g<1>+)?$%u', $lastName))
|
if(!preg_match('%^[\p{Lu}\p{Lo}]\p{Mn}?([\p{L&}\p{Lo}]\p{Mn}?){1,16}(\-\g<1>+)?$%u', $lastName))
|
||||||
throw new InvalidUserNameException;
|
throw new InvalidUserNameException;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stateChanges("last_name", $lastName);
|
$this->stateChanges("last_name", $lastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNsfwTolerance(int $tolerance): void
|
function setNsfwTolerance(int $tolerance): void
|
||||||
{
|
{
|
||||||
$this->stateChanges("nsfw_tolerance", $tolerance);
|
$this->stateChanges("nsfw_tolerance", $tolerance);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPrivacySetting(string $id, int $status): void
|
function setPrivacySetting(string $id, int $status): void
|
||||||
{
|
{
|
||||||
$this->stateChanges("privacy", bmask($this->changes["privacy"] ?? $this->getRecord()->privacy, [
|
$this->stateChanges("privacy", bmask($this->changes["privacy"] ?? $this->getRecord()->privacy, [
|
||||||
|
@ -784,7 +807,7 @@ class User extends RowModel
|
||||||
],
|
],
|
||||||
])->set($id, $status)->toInteger());
|
])->set($id, $status)->toInteger());
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLeftMenuItemStatus(string $id, bool $status): void
|
function setLeftMenuItemStatus(string $id, bool $status): void
|
||||||
{
|
{
|
||||||
$mask = bmask($this->changes["left_menu"] ?? $this->getRecord()->left_menu, [
|
$mask = bmask($this->changes["left_menu"] ?? $this->getRecord()->left_menu, [
|
||||||
|
@ -800,10 +823,10 @@ class User extends RowModel
|
||||||
"poster",
|
"poster",
|
||||||
],
|
],
|
||||||
])->set($id, (int) $status)->toInteger();
|
])->set($id, (int) $status)->toInteger();
|
||||||
|
|
||||||
$this->stateChanges("left_menu", $mask);
|
$this->stateChanges("left_menu", $mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setShortCode(?string $code = NULL, bool $force = false): ?bool
|
function setShortCode(?string $code = NULL, bool $force = false): ?bool
|
||||||
{
|
{
|
||||||
if(!is_null($code)) {
|
if(!is_null($code)) {
|
||||||
|
@ -815,20 +838,20 @@ class User extends RowModel
|
||||||
return false;
|
return false;
|
||||||
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy")
|
if(\Chandler\MVC\Routing\Router::i()->getMatchingRoute("/$code")[0]->presenter !== "UnknownTextRouteStrategy")
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
|
$pClub = DatabaseConnection::i()->getContext()->table("groups")->where("shortcode", $code)->fetch();
|
||||||
if(!is_null($pClub))
|
if(!is_null($pClub))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stateChanges("shortcode", $code);
|
$this->stateChanges("shortcode", $code);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPhoneWithVerification(string $phone): string
|
function setPhoneWithVerification(string $phone): string
|
||||||
{
|
{
|
||||||
$code = unpack("S", openssl_random_pseudo_bytes(2))[1];
|
$code = unpack("S", openssl_random_pseudo_bytes(2))[1];
|
||||||
|
|
||||||
if($this->hasPendingNumberChange()) {
|
if($this->hasPendingNumberChange()) {
|
||||||
DatabaseConnection::i()->getContext()
|
DatabaseConnection::i()->getContext()
|
||||||
->table("number_verification")
|
->table("number_verification")
|
||||||
|
@ -839,10 +862,10 @@ class User extends RowModel
|
||||||
->table("number_verification")
|
->table("number_verification")
|
||||||
->insert(["user" => $this->getId(), "number" => $phone, "code" => $code]);
|
->insert(["user" => $this->getId(), "number" => $phone, "code" => $code]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (string) $code;
|
return (string) $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
# KABOBSQL temporary fix
|
# KABOBSQL temporary fix
|
||||||
# Tuesday, the 7th of January 2020 @ 22:43 <Menhera>: implementing quick fix to this problem and monitoring
|
# Tuesday, the 7th of January 2020 @ 22:43 <Menhera>: implementing quick fix to this problem and monitoring
|
||||||
# NOTICE: this is an ongoing conversation, add your comments just above this line. Thanks!
|
# NOTICE: this is an ongoing conversation, add your comments just above this line. Thanks!
|
||||||
|
@ -850,10 +873,10 @@ class User extends RowModel
|
||||||
{
|
{
|
||||||
$this->stateChanges("shortcode", $this->getRecord()->shortcode); #fix KABOBSQL
|
$this->stateChanges("shortcode", $this->getRecord()->shortcode); #fix KABOBSQL
|
||||||
$this->stateChanges("online", $time);
|
$this->stateChanges("online", $time);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function adminNotify(string $message): bool
|
function adminNotify(string $message): bool
|
||||||
{
|
{
|
||||||
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
$admId = OPENVK_ROOT_CONF["openvk"]["preferences"]["support"]["adminAccount"];
|
||||||
|
@ -861,12 +884,12 @@ class User extends RowModel
|
||||||
return false;
|
return false;
|
||||||
else if(is_null($admin = (new Users)->get($admId)))
|
else if(is_null($admin = (new Users)->get($admId)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$cor = new Correspondence($admin, $this);
|
$cor = new Correspondence($admin, $this);
|
||||||
$msg = new Message;
|
$msg = new Message;
|
||||||
$msg->setContent($message);
|
$msg->setContent($message);
|
||||||
$cor->sendMessage($msg, true);
|
$cor->sendMessage($msg, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +916,7 @@ class User extends RowModel
|
||||||
case 2:
|
case 2:
|
||||||
return 2;
|
return 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -14,6 +14,8 @@ class Video extends Media
|
||||||
|
|
||||||
protected $tableName = "videos";
|
protected $tableName = "videos";
|
||||||
protected $fileExtension = "ogv";
|
protected $fileExtension = "ogv";
|
||||||
|
|
||||||
|
protected $processingPlaceholder = "video/rendering";
|
||||||
|
|
||||||
protected function saveFile(string $filename, string $hash): bool
|
protected function saveFile(string $filename, string $hash): bool
|
||||||
{
|
{
|
||||||
|
@ -37,11 +39,13 @@ class Video extends Media
|
||||||
throw new \DomainException("$filename does not contain any meaningful video streams");
|
throw new \DomainException("$filename does not contain any meaningful video streams");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(!is_dir($dirId = $this->pathFromHash($hash)))
|
if(!is_dir($dirId = dirname($this->pathFromHash($hash))))
|
||||||
mkdir($dirId);
|
mkdir($dirId);
|
||||||
|
|
||||||
$dir = $this->getBaseDir();
|
$dir = $this->getBaseDir();
|
||||||
Shell::bash(__DIR__ . "/../shell/processVideo.sh", OPENVK_ROOT, $filename, $dir, $hash)->start(); #async :DDD
|
$ext = Shell::isPowershell() ? "ps1" : "sh";
|
||||||
|
$cmd = Shell::isPowershell() ? "powershell" : "bash";
|
||||||
|
Shell::$cmd(__DIR__ . "/../shell/processVideo.$ext", OPENVK_ROOT, $filename, $dir, $hash)->start(); #async :DDD
|
||||||
} catch(ShellUnavailableException $suex) {
|
} catch(ShellUnavailableException $suex) {
|
||||||
exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "Shell is unavailable" : VIDEOS_FRIENDLY_ERROR);
|
exit(OPENVK_ROOT_CONF["openvk"]["debug"] ? "Shell is unavailable" : VIDEOS_FRIENDLY_ERROR);
|
||||||
} catch(UnknownCommandException $ucex) {
|
} catch(UnknownCommandException $ucex) {
|
||||||
|
@ -51,7 +55,23 @@ class Video extends Media
|
||||||
usleep(200100);
|
usleep(200100);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function checkIfFileIsProcessed(): bool
|
||||||
|
{
|
||||||
|
if($this->getType() != Video::TYPE_DIRECT)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if(!file_exists($this->getFileName())) {
|
||||||
|
if((time() - $this->getRecord()->last_checked) > 3600) {
|
||||||
|
// TODO notify that video processor is probably dead
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function getName(): string
|
function getName(): string
|
||||||
{
|
{
|
||||||
return $this->getRecord()->name;
|
return $this->getRecord()->name;
|
||||||
|
@ -81,6 +101,9 @@ class Video extends Media
|
||||||
function getThumbnailURL(): string
|
function getThumbnailURL(): string
|
||||||
{
|
{
|
||||||
if($this->getType() === Video::TYPE_DIRECT) {
|
if($this->getType() === Video::TYPE_DIRECT) {
|
||||||
|
if(!$this->isProcessed())
|
||||||
|
return "/assets/packages/static/openvk/video/rendering.apng";
|
||||||
|
|
||||||
return preg_replace("%\.[A-z]++$%", ".gif", $this->getURL());
|
return preg_replace("%\.[A-z]++$%", ".gif", $this->getURL());
|
||||||
} else {
|
} else {
|
||||||
return $this->getVideoDriver()->getThumbnailURL();
|
return $this->getVideoDriver()->getThumbnailURL();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Models\Repositories;
|
namespace openvk\Web\Models\Repositories;
|
||||||
use openvk\Web\Models\Entities\Album;
|
use openvk\Web\Models\Entities\Album;
|
||||||
|
use openvk\Web\Models\Entities\Photo;
|
||||||
use openvk\Web\Models\Entities\Club;
|
use openvk\Web\Models\Entities\Club;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
use Nette\Database\Table\ActiveRow;
|
use Nette\Database\Table\ActiveRow;
|
||||||
|
@ -115,4 +116,11 @@ class Albums
|
||||||
|
|
||||||
return new Album($album);
|
return new Album($album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAlbumByPhotoId(Photo $photo): ?Album
|
||||||
|
{
|
||||||
|
$dbalbum = $this->context->table("album_relations")->where(["media" => $photo->getId()])->fetch();
|
||||||
|
|
||||||
|
return $dbalbum->collection ? $this->get($dbalbum->collection) : null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ class Clubs
|
||||||
|
|
||||||
function getPopularClubs(): \Traversable
|
function getPopularClubs(): \Traversable
|
||||||
{
|
{
|
||||||
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 10;";
|
$query = "SELECT ROW_NUMBER() OVER (ORDER BY `subscriptions` DESC) as `place`, `target` as `id`, COUNT(`follower`) as `subscriptions` FROM `subscriptions` WHERE `model` = \"openvk\\\Web\\\Models\\\Entities\\\Club\" GROUP BY `target` ORDER BY `subscriptions` DESC, `id` LIMIT 30;";
|
||||||
$entries = DatabaseConnection::i()->getConnection()->query($query);
|
$entries = DatabaseConnection::i()->getConnection()->query($query);
|
||||||
|
|
||||||
foreach($entries as $entry)
|
foreach($entries as $entry)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Notes
|
||||||
function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
function getUserNotes(User $user, int $page = 1, ?int $perPage = NULL): \Traversable
|
||||||
{
|
{
|
||||||
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
$perPage = $perPage ?? OPENVK_DEFAULT_PER_PAGE;
|
||||||
foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->page($page, $perPage) as $album)
|
foreach($this->notes->where("owner", $user->getId())->where("deleted", 0)->order("created DESC")->page($page, $perPage) as $album)
|
||||||
yield new Note($album);
|
yield new Note($album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,6 @@ class Posts
|
||||||
{
|
{
|
||||||
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post])->fetch();
|
$post = $this->posts->where(['wall' => $wall, 'virtual_id' => $post])->fetch();
|
||||||
if(!is_null($post))
|
if(!is_null($post))
|
||||||
|
|
||||||
return new Post($post);
|
return new Post($post);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -39,7 +39,7 @@ class Users
|
||||||
function find(string $query): Util\EntityStream
|
function find(string $query): Util\EntityStream
|
||||||
{
|
{
|
||||||
$query = "%$query%";
|
$query = "%$query%";
|
||||||
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo) LIKE ?", $query)->where("deleted", 0);
|
$result = $this->users->where("CONCAT_WS(' ', first_name, last_name, pseudo, shortcode) LIKE ?", $query)->where("deleted", 0);
|
||||||
|
|
||||||
return new Util\EntityStream("User", $result);
|
return new Util\EntityStream("User", $result);
|
||||||
}
|
}
|
||||||
|
|
20
Web/Models/shell/processVideo.ps1
Normal file
20
Web/Models/shell/processVideo.ps1
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
$ovkRoot = $args[0]
|
||||||
|
$file = $args[1]
|
||||||
|
$dir = $args[2]
|
||||||
|
$hash = $args[3]
|
||||||
|
$hashT = $hash.substring(0, 2)
|
||||||
|
$temp = [System.IO.Path]::GetTempFileName()
|
||||||
|
$temp2 = [System.IO.Path]::GetTempFileName()
|
||||||
|
|
||||||
|
$shell = Get-WmiObject Win32_process -filter "ProcessId = $PID"
|
||||||
|
$shell.SetPriority(16384)
|
||||||
|
|
||||||
|
Move-Item $file $temp
|
||||||
|
|
||||||
|
# video stub logic was implicitly deprecated, so we start processing at once
|
||||||
|
ffmpeg -i $temp -ss 00:00:01.000 -vframes 1 "$dir$hashT/$hash.gif"
|
||||||
|
ffmpeg -i $temp -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y $temp2
|
||||||
|
|
||||||
|
Move-Item $temp2 "$dir$hashT/$hash.ogv"
|
||||||
|
Remove-Item $temp
|
||||||
|
Remove-Item $temp2
|
|
@ -5,7 +5,7 @@ cp ../files/video/rendering.apng $3${4:0:2}/$4.gif
|
||||||
cp ../files/video/rendering.ogv $3/${4:0:2}/$4.ogv
|
cp ../files/video/rendering.ogv $3/${4:0:2}/$4.ogv
|
||||||
|
|
||||||
nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif
|
nice ffmpeg -i "/tmp/vid_$tmpfile.bin" -ss 00:00:01.000 -vframes 1 $3${4:0:2}/$4.gif
|
||||||
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf scale=640x360,setsar=1:1 -y "/tmp/ffmOi$tmpfile.ogv"
|
nice -n 20 ffmpeg -i "/tmp/vid_$tmpfile.bin" -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 -vf "scale=640:480:force_original_aspect_ratio=decrease,pad=640:480:(ow-iw)/2:(oh-ih)/2,setsar=1" -y "/tmp/ffmOi$tmpfile.ogv"
|
||||||
|
|
||||||
rm -rf $3${4:0:2}/$4.ogv
|
rm -rf $3${4:0:2}/$4.ogv
|
||||||
mv "/tmp/ffmOi$tmpfile.ogv" $3${4:0:2}/$4.ogv
|
mv "/tmp/ffmOi$tmpfile.ogv" $3${4:0:2}/$4.ogv
|
||||||
|
|
|
@ -126,4 +126,11 @@ final class AboutPresenter extends OpenVKPresenter
|
||||||
header("Location: https://github.com/openvk/openvk#readme");
|
header("Location: https://github.com/openvk/openvk#readme");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderDev(): void
|
||||||
|
{
|
||||||
|
header("HTTP/1.1 302 Found");
|
||||||
|
header("Location: https://docs.openvk.su/");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
private function warnIfNoCommerce(): void
|
private function warnIfNoCommerce(): void
|
||||||
{
|
{
|
||||||
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
if(!OPENVK_ROOT_CONF["openvk"]["preferences"]["commerce"])
|
||||||
$this->flash("warn", "Коммерция отключена системным администратором", "Настройки ваучеров и подарков будут сохранены, но не будут оказывать никакого влияния.");
|
$this->flash("warn", tr("admin_commerce_disabled"), tr("admin_commerce_disabled_desc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function searchResults(object $repo, &$count)
|
private function searchResults(object $repo, &$count)
|
||||||
|
@ -346,7 +346,20 @@ final class AdminPresenter extends OpenVKPresenter
|
||||||
exit(json_encode([ "error" => "User does not exist" ]));
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
$user->ban($this->queryParam("reason"));
|
$user->ban($this->queryParam("reason"));
|
||||||
exit(json_encode([ "reason" => $this->queryParam("reason") ]));
|
exit(json_encode([ "success" => true, "reason" => $this->queryParam("reason") ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderQuickUnban(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$user = $this->users->get($id);
|
||||||
|
if(!$user)
|
||||||
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
|
$user->setBlock_Reason(null);
|
||||||
|
$user->save();
|
||||||
|
exit(json_encode([ "success" => true ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderQuickWarn(int $id): void
|
function renderQuickWarn(int $id): void
|
||||||
|
|
|
@ -4,6 +4,7 @@ use openvk\Web\Models\Entities\IP;
|
||||||
use openvk\Web\Models\Entities\User;
|
use openvk\Web\Models\Entities\User;
|
||||||
use openvk\Web\Models\Entities\PasswordReset;
|
use openvk\Web\Models\Entities\PasswordReset;
|
||||||
use openvk\Web\Models\Entities\EmailVerification;
|
use openvk\Web\Models\Entities\EmailVerification;
|
||||||
|
use openvk\Web\Models\Exceptions\InvalidUserNameException;
|
||||||
use openvk\Web\Models\Repositories\IPs;
|
use openvk\Web\Models\Repositories\IPs;
|
||||||
use openvk\Web\Models\Repositories\Users;
|
use openvk\Web\Models\Repositories\Users;
|
||||||
use openvk\Web\Models\Repositories\Restores;
|
use openvk\Web\Models\Repositories\Restores;
|
||||||
|
@ -88,20 +89,25 @@ final class AuthPresenter extends OpenVKPresenter
|
||||||
if (strtotime($this->postParam("birthday")) > time())
|
if (strtotime($this->postParam("birthday")) > time())
|
||||||
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
$this->flashFail("err", tr("invalid_birth_date"), tr("invalid_birth_date_comment"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user = new User;
|
||||||
|
$user->setFirst_Name($this->postParam("first_name"));
|
||||||
|
$user->setLast_Name($this->postParam("last_name"));
|
||||||
|
$user->setSex((int)($this->postParam("sex") === "female"));
|
||||||
|
$user->setEmail($this->postParam("email"));
|
||||||
|
$user->setSince(date("Y-m-d H:i:s"));
|
||||||
|
$user->setRegistering_Ip(CONNECTING_IP);
|
||||||
|
$user->setBirthday(strtotime($this->postParam("birthday")));
|
||||||
|
$user->setActivated((int)!OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']);
|
||||||
|
} catch(InvalidUserNameException $ex) {
|
||||||
|
$this->flashFail("err", tr("error"), tr("invalid_real_name"));
|
||||||
|
}
|
||||||
|
|
||||||
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
|
$chUser = ChandlerUser::create($this->postParam("email"), $this->postParam("password"));
|
||||||
if(!$chUser)
|
if(!$chUser)
|
||||||
$this->flashFail("err", tr("failed_to_register"), tr("user_already_exists"));
|
$this->flashFail("err", tr("failed_to_register"), tr("user_already_exists"));
|
||||||
|
|
||||||
$user = new User;
|
|
||||||
$user->setUser($chUser->getId());
|
$user->setUser($chUser->getId());
|
||||||
$user->setFirst_Name($this->postParam("first_name"));
|
|
||||||
$user->setLast_Name($this->postParam("last_name"));
|
|
||||||
$user->setSex((int) ($this->postParam("sex") === "female"));
|
|
||||||
$user->setEmail($this->postParam("email"));
|
|
||||||
$user->setSince(date("Y-m-d H:i:s"));
|
|
||||||
$user->setRegistering_Ip(CONNECTING_IP);
|
|
||||||
$user->setBirthday(strtotime($this->postParam("birthday")));
|
|
||||||
$user->setActivated((int) !OPENVK_ROOT_CONF['openvk']['preferences']['security']['requireEmail']);
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
if(!is_null($referer)) {
|
if(!is_null($referer)) {
|
||||||
|
|
|
@ -17,20 +17,23 @@ final class BlobPresenter extends OpenVKPresenter
|
||||||
function renderFile(/*string*/ $dir, string $name, string $format)
|
function renderFile(/*string*/ $dir, string $name, string $format)
|
||||||
{
|
{
|
||||||
$dir = $this->getDirName($dir);
|
$dir = $this->getDirName($dir);
|
||||||
$name = preg_replace("%[^a-zA-Z0-9_\-]++%", "", $name);
|
$base = realpath(OPENVK_ROOT . "/storage/$dir");
|
||||||
$path = OPENVK_ROOT . "/storage/$dir/$name.$format";
|
$path = realpath(OPENVK_ROOT . "/storage/$dir/$name.$format");
|
||||||
if(!file_exists($path)) {
|
if(!$path) # Will also check if file exists since realpath fails on ENOENT
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
} else {
|
else if(strpos($path, $path) !== 0) # Prevent directory traversal and storage container escape
|
||||||
if(isset($_SERVER["HTTP_IF_NONE_MATCH"]))
|
$this->notFound();
|
||||||
|
|
||||||
|
if(isset($_SERVER["HTTP_IF_NONE_MATCH"]))
|
||||||
exit(header("HTTP/1.1 304 Not Modified"));
|
exit(header("HTTP/1.1 304 Not Modified"));
|
||||||
|
|
||||||
header("Content-Type: " . mime_content_type($path));
|
header("Content-Type: " . mime_content_type($path));
|
||||||
header("Content-Size: " . filesize($path));
|
header("Content-Size: " . filesize($path));
|
||||||
|
header("Cache-Control: public, max-age=1210000");
|
||||||
|
header("X-Accel-Expires: 1210000");
|
||||||
header("ETag: W/\"" . hash_file("snefru", $path) . "\"");
|
header("ETag: W/\"" . hash_file("snefru", $path) . "\"");
|
||||||
|
|
||||||
readfile($path);
|
readfile($path);
|
||||||
exit;
|
exit;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,6 +204,8 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
$this->template->isXmas = intval(date('d')) >= 1 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
$this->template->isXmas = intval(date('d')) >= 1 && date('m') == 12 || intval(date('d')) <= 15 && date('m') == 1 ? true : false;
|
||||||
$this->template->isTimezoned = Session::i()->get("_timezoneOffset");
|
$this->template->isTimezoned = Session::i()->get("_timezoneOffset");
|
||||||
|
|
||||||
|
$userValidated = 0;
|
||||||
|
$cacheTime = OPENVK_ROOT_CONF["openvk"]["preferences"]["nginxCacheTime"] ?? 0;
|
||||||
if(!is_null($user)) {
|
if(!is_null($user)) {
|
||||||
$this->user = (object) [];
|
$this->user = (object) [];
|
||||||
$this->user->raw = $user;
|
$this->user->raw = $user;
|
||||||
|
@ -261,6 +263,8 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$userValidated = 1;
|
||||||
|
$cacheTime = 0; # Force no cache
|
||||||
if ($this->user->identity->onlineStatus() == 0) {
|
if ($this->user->identity->onlineStatus() == 0) {
|
||||||
$this->user->identity->setOnline(time());
|
$this->user->identity->setOnline(time());
|
||||||
$this->user->identity->save();
|
$this->user->identity->save();
|
||||||
|
@ -271,6 +275,8 @@ abstract class OpenVKPresenter extends SimplePresenter
|
||||||
$this->template->helpdeskTicketNotAnsweredCount = (new Tickets)->getTicketCount(0);
|
$this->template->helpdeskTicketNotAnsweredCount = (new Tickets)->getTicketCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header("X-OpenVK-User-Validated: $userValidated");
|
||||||
|
header("X-Accel-Expires: $cacheTime");
|
||||||
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
setlocale(LC_TIME, ...(explode(";", tr("__locale"))));
|
||||||
|
|
||||||
parent::onStartup();
|
parent::onStartup();
|
||||||
|
|
|
@ -72,6 +72,8 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
if(empty($this->postParam("name")))
|
if(empty($this->postParam("name")))
|
||||||
$this->flashFail("err", tr("error"), tr("error_segmentation"));
|
$this->flashFail("err", tr("error"), tr("error_segmentation"));
|
||||||
|
else if(strlen($this->postParam("name")) > 36)
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||||
|
|
||||||
$album = new Album;
|
$album = new Album;
|
||||||
$album->setOwner(isset($club) ? $club->getId() * -1 : $this->user->id);
|
$album->setOwner(isset($club) ? $club->getId() * -1 : $this->user->id);
|
||||||
|
@ -100,6 +102,9 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
$this->template->album = $album;
|
$this->template->album = $album;
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
if(strlen($this->postParam("name")) > 36)
|
||||||
|
$this->flashFail("err", tr("error"), tr("error_data_too_big", "name", 36, "bytes"));
|
||||||
|
|
||||||
$album->setName(empty($this->postParam("name")) ? $album->getName() : $this->postParam("name"));
|
$album->setName(empty($this->postParam("name")) ? $album->getName() : $this->postParam("name"));
|
||||||
$album->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
$album->setDescription(empty($this->postParam("desc")) ? NULL : $this->postParam("desc"));
|
||||||
$album->setEdited(time());
|
$album->setEdited(time());
|
||||||
|
@ -276,6 +281,8 @@ final class PhotosPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
$photo->isolate();
|
$photo->isolate();
|
||||||
$photo->delete();
|
$photo->delete();
|
||||||
exit("Фотография успешно удалена!");
|
|
||||||
|
$this->flash("succ", "Фотография удалена", "Эта фотография была успешно удалена.");
|
||||||
|
$this->redirect("/id0", static::REDIRECT_TEMPORARY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ final class SearchPresenter extends OpenVKPresenter
|
||||||
$type = $this->queryParam("type") ?? "users";
|
$type = $this->queryParam("type") ?? "users";
|
||||||
$page = (int) ($this->queryParam("p") ?? 1);
|
$page = (int) ($this->queryParam("p") ?? 1);
|
||||||
|
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
if($query != "")
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
// https://youtu.be/pSAWM5YuXx8
|
// https://youtu.be/pSAWM5YuXx8
|
||||||
|
|
||||||
$repos = [ "groups" => "clubs", "users" => "users" ];
|
$repos = [ "groups" => "clubs", "users" => "users" ];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
namespace openvk\Web\Presenters;
|
namespace openvk\Web\Presenters;
|
||||||
use openvk\Web\Models\Entities\Ticket;
|
use openvk\Web\Models\Entities\Ticket;
|
||||||
use openvk\Web\Models\Repositories\Tickets;
|
use openvk\Web\Models\Repositories\{Tickets, Users};
|
||||||
use openvk\Web\Models\Entities\TicketComment;
|
use openvk\Web\Models\Entities\TicketComment;
|
||||||
use openvk\Web\Models\Repositories\TicketComments;
|
use openvk\Web\Models\Repositories\TicketComments;
|
||||||
use openvk\Web\Util\Telegram;
|
use openvk\Web\Util\Telegram;
|
||||||
|
@ -34,7 +34,13 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
$this->template->tickets = $this->tickets->getTicketsByUserId($this->user->id, $this->template->page);
|
$this->template->tickets = $this->tickets->getTicketsByUserId($this->user->id, $this->template->page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($this->template->mode === "new")
|
||||||
|
$this->template->banReason = $this->user->identity->getBanInSupportReason();
|
||||||
|
|
||||||
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
if($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
if($this->user->identity->isBannedInSupport())
|
||||||
|
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||||
|
|
||||||
if(!empty($this->postParam("name")) && !empty($this->postParam("text"))) {
|
if(!empty($this->postParam("name")) && !empty($this->postParam("text"))) {
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
@ -268,4 +274,32 @@ final class SupportPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
exit(header("HTTP/1.1 200 OK"));
|
exit(header("HTTP/1.1 200 OK"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function renderQuickBanInSupport(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$user = (new Users)->get($id);
|
||||||
|
if(!$user)
|
||||||
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
|
$user->setBlock_In_Support_Reason($this->queryParam("reason"));
|
||||||
|
$user->save();
|
||||||
|
$this->returnJson([ "success" => true, "reason" => $this->queryParam("reason") ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderQuickUnbanInSupport(int $id): void
|
||||||
|
{
|
||||||
|
$this->assertPermission("openvk\Web\Models\Entities\TicketReply", "write", 0);
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$user = (new Users)->get($id);
|
||||||
|
if(!$user)
|
||||||
|
exit(json_encode([ "error" => "User does not exist" ]));
|
||||||
|
|
||||||
|
$user->setBlock_In_Support_Reason(null);
|
||||||
|
$user->save();
|
||||||
|
$this->returnJson([ "success" => true ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
{
|
{
|
||||||
$user = $this->users->get($id);
|
$user = $this->users->get($id);
|
||||||
if(!$user || $user->isDeleted())
|
if(!$user || $user->isDeleted())
|
||||||
$this->notFound();
|
$this->template->_template = "User/deleted.xml";
|
||||||
else {
|
else {
|
||||||
if($user->getShortCode())
|
if($user->getShortCode())
|
||||||
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
|
if(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH) !== "/" . $user->getShortCode())
|
||||||
|
@ -285,7 +285,6 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$photo->setCreated(time());
|
$photo->setCreated(time());
|
||||||
$photo->save();
|
$photo->save();
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$name = $album->getName();
|
|
||||||
$this->flashFail("err", tr("error"), tr("error_upload_failed"));
|
$this->flashFail("err", tr("error"), tr("error_upload_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +481,22 @@ final class UserPresenter extends OpenVKPresenter
|
||||||
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
|
$this->flashFail("succ", tr("information_-1"), tr("two_factor_authentication_disabled_message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderResetThemepack(): void
|
||||||
|
{
|
||||||
|
$this->assertNoCSRF();
|
||||||
|
|
||||||
|
$this->setSessionTheme(Themepacks::DEFAULT_THEME_ID);
|
||||||
|
|
||||||
|
if($this->user) {
|
||||||
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
|
$this->user->identity->setStyle(Themepacks::DEFAULT_THEME_ID);
|
||||||
|
$this->user->identity->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect("/", static::REDIRECT_TEMPORARY_PRESISTENT);
|
||||||
|
}
|
||||||
|
|
||||||
function renderCoinsTransfer(): void
|
function renderCoinsTransfer(): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
$this->assertUserLoggedIn();
|
||||||
|
|
|
@ -77,6 +77,92 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
exit; # Terminate request processing as this is definitely a CORS preflight request.
|
exit; # Terminate request processing as this is definitely a CORS preflight request.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderPhotoUpload(string $signature): void
|
||||||
|
{
|
||||||
|
$secret = CHANDLER_ROOT_CONF["security"]["secret"];
|
||||||
|
$computedSignature = hash_hmac("sha3-224", $_SERVER["QUERY_STRING"], $secret);
|
||||||
|
if(!(strlen($signature) == 56 && sodium_memcmp($signature, $computedSignature) == 0)) {
|
||||||
|
header("HTTP/1.1 422 Unprocessable Entity");
|
||||||
|
exit("Try harder <3");
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = unpack("vDOMAIN/Z10FIELD/vMF/vMP/PTIME/PUSER/PGROUP", base64_decode($_SERVER["QUERY_STRING"]));
|
||||||
|
if((time() - $data["TIME"]) > 600) {
|
||||||
|
header("HTTP/1.1 422 Unprocessable Entity");
|
||||||
|
exit("Expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
$folder = __DIR__ . "../../tmp/api-storage/photos";
|
||||||
|
$maxSize = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFileSize"];
|
||||||
|
$maxFiles = OPENVK_ROOT_CONF["openvk"]["preferences"]["uploads"]["api"]["maxFilesPerDomain"];
|
||||||
|
$usrFiles = sizeof(glob("$folder/$data[USER]_*.oct"));
|
||||||
|
if($usrFiles >= $maxFiles) {
|
||||||
|
header("HTTP/1.1 507 Insufficient Storage");
|
||||||
|
exit("There are $maxFiles pending already. Please save them before uploading more :3");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Not multifile
|
||||||
|
if($data["MF"] === 0) {
|
||||||
|
$file = $_FILES[$data["FIELD"]];
|
||||||
|
if(!$file) {
|
||||||
|
header("HTTP/1.0 400");
|
||||||
|
exit("No file");
|
||||||
|
} else if($file["error"] != UPLOAD_ERR_OK) {
|
||||||
|
header("HTTP/1.0 500");
|
||||||
|
exit("File could not be consumed");
|
||||||
|
} else if($file["size"] > $maxSize) {
|
||||||
|
header("HTTP/1.0 507 Insufficient Storage");
|
||||||
|
exit("File is too big");
|
||||||
|
}
|
||||||
|
|
||||||
|
move_uploaded_file($file["tmp_name"], "$folder/$data[USER]_" . ($usrFiles + 1) . ".oct");
|
||||||
|
header("HTTP/1.0 202 Accepted");
|
||||||
|
|
||||||
|
$photo = $data["USER"] . "|" . ($usrFiles + 1) . "|" . $data["GROUP"];
|
||||||
|
exit(json_encode([
|
||||||
|
"server" => "ephemeral",
|
||||||
|
"photo" => $photo,
|
||||||
|
"hash" => hash_hmac("sha3-224", $photo, $secret),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = [];
|
||||||
|
for($i = 1; $i <= 5; $i++) {
|
||||||
|
$file = $_FILES[$data["FIELD"] . $i] ?? NULL;
|
||||||
|
if (!$file || $file["error"] != UPLOAD_ERR_OK || $file["size"] > $maxSize) {
|
||||||
|
continue;
|
||||||
|
} else if((sizeof($files) + $usrFiles) > $maxFiles) {
|
||||||
|
# Clear uploaded files since they can't be saved anyway
|
||||||
|
foreach($files as $f)
|
||||||
|
unlink($f);
|
||||||
|
|
||||||
|
header("HTTP/1.1 507 Insufficient Storage");
|
||||||
|
exit("There are $maxFiles pending already. Please save them before uploading more :3");
|
||||||
|
}
|
||||||
|
|
||||||
|
$files[++$usrFiles] = move_uploaded_file($file["tmp_name"], "$folder/$data[USER]_$usrFiles.oct");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sizeof($files) === 0) {
|
||||||
|
header("HTTP/1.0 400");
|
||||||
|
exit("No file");
|
||||||
|
}
|
||||||
|
|
||||||
|
$filesManifest = [];
|
||||||
|
foreach($files as $id => $file)
|
||||||
|
$filesManifest[] = ["keyholder" => $data["USER"], "resource" => $id, "club" => $data["GROUP"]];
|
||||||
|
|
||||||
|
$filesManifest = json_encode($filesManifest);
|
||||||
|
$manifestHash = hash_hmac("sha3-224", $filesManifest, $secret);
|
||||||
|
header("HTTP/1.0 202 Accepted");
|
||||||
|
exit(json_encode([
|
||||||
|
"server" => "ephemeral",
|
||||||
|
"photos_list" => $filesManifest,
|
||||||
|
"album_id" => "undefined",
|
||||||
|
"hash" => $manifestHash,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
function renderRoute(string $object, string $method): void
|
function renderRoute(string $object, string $method): void
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
function renderWall(int $user, bool $embedded = false): void
|
function renderWall(int $user, bool $embedded = false): void
|
||||||
{
|
{
|
||||||
if(false)
|
if(false)
|
||||||
exit("Ошибка доступа: " . (string) random_int(0, 255));
|
exit(tr("forbidden") . ": " . (string) random_int(0, 255));
|
||||||
|
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
|
@ -54,7 +54,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(!$owner->isBanned())
|
if(!$owner->isBanned())
|
||||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else
|
else
|
||||||
$this->flashFail("err", tr("error"), "Ошибка доступа");
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
} else if($user < 0) {
|
} else if($user < 0) {
|
||||||
if($owner->canBeModifiedBy($this->user->identity))
|
if($owner->canBeModifiedBy($this->user->identity))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
|
@ -89,7 +89,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
function renderRSS(int $user): void
|
function renderRSS(int $user): void
|
||||||
{
|
{
|
||||||
if(false)
|
if(false)
|
||||||
exit("Ошибка доступа: " . (string) random_int(0, 255));
|
exit(tr("forbidden") . ": " . (string) random_int(0, 255));
|
||||||
|
|
||||||
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
$owner = ($user < 0 ? (new Clubs) : (new Users))->get(abs($user));
|
||||||
if(is_null($this->user)) {
|
if(is_null($this->user)) {
|
||||||
|
@ -98,7 +98,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
if(!$owner->isBanned())
|
if(!$owner->isBanned())
|
||||||
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $owner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else
|
else
|
||||||
$this->flashFail("err", tr("error"), "Ошибка доступа");
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
} else if($user < 0) {
|
} else if($user < 0) {
|
||||||
if($owner->canBeModifiedBy($this->user->identity))
|
if($owner->canBeModifiedBy($this->user->identity))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
|
@ -213,12 +213,12 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->willExecuteWriteAction();
|
$this->willExecuteWriteAction();
|
||||||
|
|
||||||
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
||||||
?? $this->flashFail("err", "Не удалось опубликовать пост", "Такого пользователя не существует.");
|
?? $this->flashFail("err", tr("failed_to_publish_post"), tr("error_4"));
|
||||||
if($wall > 0) {
|
if($wall > 0) {
|
||||||
if(!$wallOwner->isBanned())
|
if(!$wallOwner->isBanned())
|
||||||
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
$canPost = $wallOwner->getPrivacyPermission("wall.write", $this->user->identity);
|
||||||
else
|
else
|
||||||
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
|
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||||
} else if($wall < 0) {
|
} else if($wall < 0) {
|
||||||
if($wallOwner->canBeModifiedBy($this->user->identity))
|
if($wallOwner->canBeModifiedBy($this->user->identity))
|
||||||
$canPost = true;
|
$canPost = true;
|
||||||
|
@ -229,7 +229,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$canPost)
|
if(!$canPost)
|
||||||
$this->flashFail("err", "Ошибка доступа", "Вам нельзя писать на эту стену.");
|
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||||
|
|
||||||
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
$anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"];
|
||||||
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
if($wallOwner instanceof Club && $this->postParam("as_group") === "on" && $this->postParam("force_sign") !== "on" && $anon) {
|
||||||
|
@ -265,10 +265,18 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
} catch(ISE $ex) {
|
} catch(ISE $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён или слишком велик.");
|
$this->flashFail("err", "Не удалось опубликовать пост", "Файл медиаконтента повреждён или слишком велик.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($_FILES["_vid_attachment"]["error"] === UPLOAD_ERR_OK) {
|
||||||
|
$video = Video::fastMake($this->user->id, $this->postParam("text"), $_FILES["_vid_attachment"], $anon);
|
||||||
|
}
|
||||||
|
} catch(\DomainException $ex) {
|
||||||
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted"));
|
||||||
|
} catch(ISE $ex) {
|
||||||
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("media_file_corrupted_or_too_large"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && empty($photos))
|
if(empty($this->postParam("text")) && !$photo && !$video)
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Пост пустой или слишком большой.");
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$post = new Post;
|
$post = new Post;
|
||||||
|
@ -281,7 +289,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$post->setNsfw($this->postParam("nsfw") === "on");
|
$post->setNsfw($this->postParam("nsfw") === "on");
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch (\LengthException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->flashFail("err", "Не удалось опубликовать пост", "Пост слишком большой.");
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($photos as $photo) {
|
foreach($photos as $photo) {
|
||||||
|
@ -300,8 +308,6 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
|
|
||||||
function renderPost(int $wall, int $post_id): void
|
function renderPost(int $wall, int $post_id): void
|
||||||
{
|
{
|
||||||
$this->assertUserLoggedIn();
|
|
||||||
|
|
||||||
$post = $this->posts->getPostById($wall, $post_id);
|
$post = $this->posts->getPostById($wall, $post_id);
|
||||||
if(!$post || $post->isDeleted())
|
if(!$post || $post->isDeleted())
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
@ -313,7 +319,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
$this->template->wallOwner = (new Users)->get($post->getTargetWall());
|
||||||
$this->template->isWallOfGroup = false;
|
$this->template->isWallOfGroup = false;
|
||||||
if($this->template->wallOwner->isBanned())
|
if($this->template->wallOwner->isBanned())
|
||||||
$this->flashFail("err", tr("error"), "Ошибка доступа");
|
$this->flashFail("err", tr("error"), tr("forbidden"));
|
||||||
} else {
|
} else {
|
||||||
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
$this->template->wallOwner = (new Clubs)->get(abs($post->getTargetWall()));
|
||||||
$this->template->isWallOfGroup = true;
|
$this->template->isWallOfGroup = true;
|
||||||
|
@ -377,7 +383,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$user = $this->user->id;
|
$user = $this->user->id;
|
||||||
|
|
||||||
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
$wallOwner = ($wall > 0 ? (new Users)->get($wall) : (new Clubs)->get($wall * -1))
|
||||||
?? $this->flashFail("err", "Не удалось удалить пост", "Такого пользователя не существует.");
|
?? $this->flashFail("err", tr("failed_to_delete_post"), tr("error_4"));
|
||||||
|
|
||||||
if($wall < 0) $canBeDeletedByOtherUser = $wallOwner->canBeModifiedBy($this->user->identity);
|
if($wall < 0) $canBeDeletedByOtherUser = $wallOwner->canBeModifiedBy($this->user->identity);
|
||||||
else $canBeDeletedByOtherUser = false;
|
else $canBeDeletedByOtherUser = false;
|
||||||
|
@ -388,7 +394,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$post->delete();
|
$post->delete();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->flashFail("err", "Не удалось удалить пост", "Вы не вошли в аккаунт.");
|
$this->flashFail("err", tr("failed_to_delete_post"), tr("login_required_error_comment"));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->redirect($wall < 0 ? "/club".($wall*-1) : "/id".$wall, static::REDIRECT_TEMPORARY);
|
$this->redirect($wall < 0 ? "/club".($wall*-1) : "/id".$wall, static::REDIRECT_TEMPORARY);
|
||||||
|
@ -405,7 +411,7 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->notFound();
|
$this->notFound();
|
||||||
|
|
||||||
if(!$post->canBePinnedBy($this->user->identity))
|
if(!$post->canBePinnedBy($this->user->identity))
|
||||||
$this->flashFail("err", "Ошибка доступа", "Вам нельзя закреплять этот пост.");
|
$this->flashFail("err", tr("not_enough_permissions"), tr("not_enough_permissions_comment"));
|
||||||
|
|
||||||
if(($this->queryParam("act") ?? "pin") === "pin") {
|
if(($this->queryParam("act") ?? "pin") === "pin") {
|
||||||
$post->pin();
|
$post->pin();
|
||||||
|
@ -414,6 +420,6 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO localize message based on language and ?act=(un)pin
|
// TODO localize message based on language and ?act=(un)pin
|
||||||
$this->flashFail("succ", "Операция успешна", "Операция успешна.");
|
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container_gray">
|
<div class="container_gray">
|
||||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
|
|
||||||
{if sizeof($data) > 0}
|
{if sizeof($data) > 0}
|
||||||
<div class="content" n:foreach="$data as $dat">
|
<div class="content" n:foreach="$data as $dat">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{block wrap}
|
{block wrap}
|
||||||
<div class="ovk-lw-container">
|
<div class="ovk-lw-container">
|
||||||
<div class="ovk-lw--list">
|
<div class="ovk-lw--list">
|
||||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
|
|
||||||
{if sizeof($data) > 0}
|
{if sizeof($data) > 0}
|
||||||
<table n:foreach="$data as $dat" border="0" style="font-size:11px;" class="post">
|
<table n:foreach="$data as $dat" border="0" style="font-size:11px;" class="post">
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{var instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||||
|
{if !isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html n:if="!isset($parentModule) || substr($parentModule, 0, 21) === 'libchandler:absolute.'">
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>
|
<title>
|
||||||
{ifset title}{include title} - {/ifset}{$instance_name}
|
{ifset title}{include title} - {/ifset}{$instance_name}
|
||||||
|
@ -98,12 +98,12 @@
|
||||||
|
|
||||||
<div class="layout">
|
<div class="layout">
|
||||||
<div id="xhead" class="dm"></div>
|
<div id="xhead" class="dm"></div>
|
||||||
<div class="page_header {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}page_custom_header{/if}">
|
<div class="page_header{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME} page_custom_header{/if}">
|
||||||
<a href="/" class="home_button {if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}home_button_custom{/if}" title="{$instance_name}">{$instance_name}</a>
|
<a href="/" class="home_button{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME} home_button_custom{/if}" title="{$instance_name}">{if $instance_name != OPENVK_DEFAULT_INSTANCE_NAME}{$instance_name}{/if}</a>
|
||||||
<div n:if="isset($thisUser) ? (!$thisUser->isBanned() XOR !$thisUser->isActivated()) : true" class="header_navigation">
|
<div n:if="isset($thisUser) ? (!$thisUser->isBanned() XOR !$thisUser->isActivated()) : true" class="header_navigation">
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<a href="/">{_header_home}</a>
|
<a href="/" title="[Alt+Shift+,]" accesskey=",">{_header_home}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<a href="/search?type=groups">{_header_groups}</a>
|
<a href="/search?type=groups">{_header_groups}</a>
|
||||||
|
@ -122,14 +122,14 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<form action="/search" method="get">
|
<form action="/search" method="get">
|
||||||
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" />
|
<input type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{else}
|
{else}
|
||||||
<div class="link">
|
<div class="link">
|
||||||
<a href="/login">{_header_login}</a>
|
<a href="/login">{_header_login}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable']" class="link">
|
<div class="link">
|
||||||
<a href="/reg">{_header_registration}</a>
|
<a href="/reg">{_header_registration}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="link">
|
<div class="link">
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
{ifset $thisUser}
|
{ifset $thisUser}
|
||||||
{if !$thisUser->isBanned() XOR !$thisUser->isActivated()}
|
{if !$thisUser->isBanned() XOR !$thisUser->isActivated()}
|
||||||
<a href="/edit" class="link edit-button">{_edit_button}</a>
|
<a href="/edit" class="link edit-button">{_edit_button}</a>
|
||||||
<a href="{$thisUser->getURL()}" class="link">{_my_page}</a>
|
<a href="{$thisUser->getURL()}" class="link" title="{_my_page} [Alt+Shift+.]" accesskey=".">{_my_page}</a>
|
||||||
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
|
<a href="/friends{$thisUser->getId()}" class="link">{_my_friends}
|
||||||
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
<object type="internal/link" n:if="$thisUser->getFollowersCount() > 0">
|
||||||
<a href="/friends{$thisUser->getId()}?act=incoming">
|
<a href="/friends{$thisUser->getId()}?act=incoming">
|
||||||
|
@ -161,19 +161,19 @@
|
||||||
</a>
|
</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('notes')" href="/notes{$thisUser->getId()}" class="link">{_my_notes}</a>
|
<a n:if="$thisUser->getLeftMenuItemStatus('notes')" href="/notes{$thisUser->getId()}" class="link">{_my_notes}</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('groups')" href="/groups{$thisUser->getId()}" class="link">{_my_groups}</a>
|
<a n:if="$thisUser->getLeftMenuItemStatus('groups')" href="/groups{$thisUser->getId()}" class="link">{_my_groups}</a>
|
||||||
<a n:if="$thisUser->getLeftMenuItemStatus('news')" href="/feed" class="link">{_my_feed}</a>
|
<a n:if="$thisUser->getLeftMenuItemStatus('news')" href="/feed" class="link" title="{_my_feed} [Alt+Shift+W]" accesskey="w">{_my_feed}</a>
|
||||||
<a href="/notifications" class="link">{_my_feedback}
|
<a href="/notifications" class="link" title="{_my_feedback} [Alt+Shift+N]" accesskey="n">{_my_feedback}
|
||||||
{if $thisUser->getNotificationsCount() > 0}
|
{if $thisUser->getNotificationsCount() > 0}
|
||||||
(<b>{$thisUser->getNotificationsCount()}</b>)
|
(<b>{$thisUser->getNotificationsCount()}</b>)
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
<a href="/settings" class="link">{_my_settings}</a>
|
<a href="/settings" class="link">{_my_settings}</a>
|
||||||
|
|
||||||
{var canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
{var $canAccessAdminPanel = $thisUser->getChandlerUser()->can("access")->model("admin")->whichBelongsTo(NULL)}
|
||||||
{var canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{var $canAccessHelpdesk = $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
{var menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
{var $menuLinksAvaiable = sizeof(OPENVK_ROOT_CONF['openvk']['preferences']['menu']['links']) > 0 && $thisUser->getLeftMenuItemStatus('links')}
|
||||||
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
<div n:if="$canAccessAdminPanel || $canAccessHelpdesk || $menuLinksAvaiable" class="menu_divider"></div>
|
||||||
<a href="/admin" class="link" n:if="$canAccessAdminPanel">Админ-панель</a>
|
<a href="/admin" class="link" n:if="$canAccessAdminPanel" title="{_admin} [Alt+Shift+A]" accesskey="a">{_admin}</a>
|
||||||
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">Helpdesk
|
<a href="/support/tickets" class="link" n:if="$canAccessHelpdesk">Helpdesk
|
||||||
{if $helpdeskTicketNotAnsweredCount > 0}
|
{if $helpdeskTicketNotAnsweredCount > 0}
|
||||||
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
(<b>{$helpdeskTicketNotAnsweredCount}</b>)
|
||||||
|
@ -186,6 +186,14 @@
|
||||||
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
<div n:if="$thisUser->getPinnedClubCount() > 0" class="menu_divider"></div>
|
||||||
<a n:foreach="$thisUser->getPinnedClubs() as $club" href="{$club->getURL()}" class="link group_link">{$club->getName()}</a>
|
<a n:foreach="$thisUser->getPinnedClubs() as $club" href="{$club->getURL()}" class="link group_link">{$club->getName()}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $thisUser->getCoins() != 0" id="votesBalance">
|
||||||
|
{tr("you_still_have_x_points", $thisUser->getCoins())|noescape}
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<a href="/settings?act=finance">{_top_up_your_account} »</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable'] && $thisUser->getLeftMenuItemStatus('poster')" href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
|
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['enable'] && $thisUser->getLeftMenuItemStatus('poster')" href="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['link']}" >
|
||||||
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" />
|
<img src="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['src']}" alt="{php echo OPENVK_ROOT_CONF['openvk']['preferences']['adPoster']['caption']}" class="psa-poster" style="max-width: 100%; margin-top: 50px;" />
|
||||||
</a>
|
</a>
|
||||||
|
@ -208,7 +216,7 @@
|
||||||
<input type="hidden" name="jReturnTo" value="{$_SERVER['REQUEST_URI']}" />
|
<input type="hidden" name="jReturnTo" value="{$_SERVER['REQUEST_URI']}" />
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<input type="submit" value="{_log_in}" class="button" style="display: inline-block;" />
|
<input type="submit" value="{_log_in}" class="button" style="display: inline-block;" />
|
||||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable']" href="/reg" class="button" style="display: inline-block;">{_registration}</a><br><br>
|
<a href="/reg" class="button" style="display: inline-block;">{_registration}</a><br><br>
|
||||||
<a href="/restore">{_forgot_password}</a>
|
<a href="/restore">{_forgot_password}</a>
|
||||||
</form>
|
</form>
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
@ -251,7 +259,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page_footer">
|
<div class="page_footer">
|
||||||
{var dbVersion = \Chandler\Database\DatabaseConnection::i()->getConnection()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION)}
|
{var $dbVersion = \Chandler\Database\DatabaseConnection::i()->getConnection()->getPdo()->getAttribute(\PDO::ATTR_SERVER_VERSION)}
|
||||||
|
|
||||||
<div class="navigation_footer">
|
<div class="navigation_footer">
|
||||||
<a href="/about" class="link">{_footer_about_instance}</a>
|
<a href="/about" class="link">{_footer_about_instance}</a>
|
||||||
|
@ -288,12 +296,44 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']" async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}" src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['enable']" async defer data-domain="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['domain']}" src="{php echo OPENVK_ROOT_CONF['openvk']['telemetry']['plausible']['server']}js/plausible.js"></script>
|
||||||
|
|
||||||
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['piwik']['enable']">
|
||||||
|
{var $piwik = (object) OPENVK_ROOT_CONF['openvk']['telemetry']['piwik']}
|
||||||
|
|
||||||
|
//<![CDATA[
|
||||||
|
(function(window,document,dataLayerName,id){
|
||||||
|
window[dataLayerName]=window[dataLayerName]||[],window[dataLayerName].push({ start:(new Date).getTime(),event:"stg.start" });var scripts=document.getElementsByTagName('script')[0],tags=document.createElement('script');
|
||||||
|
function stgCreateCookie(a,b,c){ var d="";if(c){ var e=new Date;e.setTime(e.getTime()+24*c*60*60*1e3),d=";expires="+e.toUTCString() }document.cookie=a+"="+b+d+";path=/" }
|
||||||
|
var isStgDebug=(window.location.href.match("stg_debug")||document.cookie.match("stg_debug"))&&!window.location.href.match("stg_disable_debug");stgCreateCookie("stg_debug",isStgDebug?1:"",isStgDebug?14:-1);
|
||||||
|
var qP=[];dataLayerName!=="dataLayer"&&qP.push("data_layer_name="+dataLayerName),isStgDebug&&qP.push("stg_debug");var qPString=qP.length>0?("?"+qP.join("&")):"";
|
||||||
|
tags.async=!0,tags.src={$piwik->container . "/"}+id+".js"+qPString,scripts.parentNode.insertBefore(tags,scripts);
|
||||||
|
!function(a,n,i){ a[n]=a[n]||{ };for(var c=0;c<i.length;c++)!function(i){ a[n][i]=a[n][i]||{ },a[n][i].api=a[n][i].api||function(){ var a=[].slice.call(arguments,0);"string"==typeof a[0]&&window[dataLayerName].push({ event:n+"."+i+":"+a[0],parameters:[].slice.call(arguments,1) }) } }(i[c]) }(window,"ppms",["tm","cm"]);
|
||||||
|
})(window,document,{$piwik->layer}, {$piwik->site});
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script n:if="OPENVK_ROOT_CONF['openvk']['telemetry']['matomo']['enable']">
|
||||||
|
{var $matomo = (object) OPENVK_ROOT_CONF['openvk']['telemetry']['matomo']}
|
||||||
|
//<![CDATA[
|
||||||
|
var _paq = window._paq = window._paq || [];
|
||||||
|
_paq.push(['trackPageView']);
|
||||||
|
_paq.push(['enableLinkTracking']);
|
||||||
|
(function() {
|
||||||
|
var u="//" + {$matomo->container} + "/";
|
||||||
|
_paq.push(['setTrackerUrl', u+'matomo.php']);
|
||||||
|
_paq.push(['setSiteId', {$matomo->site}]);
|
||||||
|
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
|
||||||
|
g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
|
||||||
|
})();
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
|
||||||
{ifset bodyScripts}
|
{ifset bodyScripts}
|
||||||
{include bodyScripts}
|
{include bodyScripts}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{if isset($parentModule) && substr($parentModule, 0, 21) !== 'libchandler:absolute.'}
|
{if isset($parentModule) && substr($parentModule, 0, 21) !== 'libchandler:absolute.'}
|
||||||
<!-- INCLUDING TEMPLATE FROM PARENTMODULE: {$parentModule} -->
|
<!-- INCLUDING TEMPLATE FROM PARENTMODULE: {$parentModule} -->
|
||||||
|
|
|
@ -1,70 +1,74 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
{block wrap}
|
{block wrap}
|
||||||
<div class="page_wrap">
|
<div class="wrap2">
|
||||||
<div n:ifset="tabs" n:ifcontent class="tabs">
|
<div class="wrap1">
|
||||||
{include tabs}
|
<div class="page_wrap padding_top">
|
||||||
</div>
|
<div n:ifset="tabs" class="tabs">
|
||||||
|
{include tabs}
|
||||||
|
</div>
|
||||||
|
|
||||||
{ifset size}
|
{ifset size}
|
||||||
{include size, x => $dat}
|
{include size, x => $dat}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
|
||||||
{ifset specpage}
|
|
||||||
{include specpage, x => $dat}
|
|
||||||
{else}
|
|
||||||
<div class="container_gray">
|
|
||||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
|
||||||
|
|
||||||
{if sizeof($data) > 0}
|
{ifset specpage}
|
||||||
<div class="content" n:foreach="$data as $dat">
|
{include specpage, x => $dat}
|
||||||
<table>
|
{else}
|
||||||
<tbody>
|
<div class="container_gray">
|
||||||
<tr>
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
<td valign="top">
|
|
||||||
<a href="{include link, x => $dat}">
|
{if sizeof($data) > 0}
|
||||||
{include preview, x => $dat}
|
<div class="content" n:foreach="$data as $dat">
|
||||||
</a>
|
<table>
|
||||||
</td>
|
<tbody>
|
||||||
<td valign="top" style="width: 100%">
|
<tr>
|
||||||
{ifset infoTable}
|
<td valign="top">
|
||||||
{include infoTable, x => $dat}
|
<a href="{include link, x => $dat}">
|
||||||
{else}
|
{include preview, x => $dat}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td valign="top" style="width: 100%">
|
||||||
|
{ifset infotable}
|
||||||
|
{include infotable, x => $dat}
|
||||||
|
{else}
|
||||||
<a href="{include link, x => $dat}">
|
<a href="{include link, x => $dat}">
|
||||||
<b>
|
<b>
|
||||||
{include name, x => $dat}
|
{include name, x => $dat}
|
||||||
</b>
|
</b>
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br/>
|
||||||
{include description, x => $dat}
|
{include description, x => $dat}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
</td>
|
</td>
|
||||||
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px; text-transform: lowercase;">
|
<td n:ifset="actions" valign="top" class="action_links" style="width: 150px; text-transform: lowercase;">
|
||||||
{include actions, x => $dat}
|
{include actions, x => $dat}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{include "components/paginator.xml", conf => (object) [
|
{include "components/paginator.xml", conf => (object) [
|
||||||
"page" => $page,
|
"page" => $page,
|
||||||
"count" => $count,
|
"count" => $count,
|
||||||
"amount" => sizeof($data),
|
"amount" => sizeof($data),
|
||||||
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
"perPage" => $perPage ?? OPENVK_DEFAULT_PER_PAGE,
|
||||||
"atBottom" => true,
|
"atBottom" => true,
|
||||||
]}
|
]}
|
||||||
{else}
|
|
||||||
{ifset customErrorMessage}
|
|
||||||
{include customErrorMessage}
|
|
||||||
{else}
|
{else}
|
||||||
{include "components/nothing.xml"}
|
{ifset customErrorMessage}
|
||||||
{/ifset}
|
{include customErrorMessage}
|
||||||
{/if}
|
{else}
|
||||||
</div>
|
{include "components/nothing.xml"}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/ifset}
|
||||||
|
|
||||||
{ifset bottom}
|
{ifset bottom}
|
||||||
{include bottom}
|
{include bottom}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
</div>
|
||||||
|
{/block}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<table width="100%" cellspacing="0" cellpadding="0">
|
<table width="100%" cellspacing="0" cellpadding="0">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<td width="250" {if sizeof($admins) > 0}style="padding-right: 10px;"{/if}>
|
<td width="250"{if sizeof($admins) > 0} style="padding-right: 10px;"{/if}>
|
||||||
<h4>{_statistics}</h4>
|
<h4>{_statistics}</h4>
|
||||||
<div style="margin-top: 5px;">
|
<div style="margin-top: 5px;">
|
||||||
{_on_this_instance_are}
|
{_on_this_instance_are}
|
||||||
|
@ -21,6 +21,15 @@
|
||||||
<li><span>{tr("about_wall_posts", $postsCount)|noescape}</span></li>
|
<li><span>{tr("about_wall_posts", $postsCount)|noescape}</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{if OPENVK_ROOT_CONF['openvk']['preferences']['about']['links']}
|
||||||
|
<h4>{_about_links}</h4>
|
||||||
|
<div style="margin-top: 5px;">
|
||||||
|
{_instance_links}
|
||||||
|
<ul>
|
||||||
|
<li n:foreach="OPENVK_ROOT_CONF['openvk']['preferences']['about']['links'] as $aboutLink"><a href="{$aboutLink['url']}" target="_blank" class="link">{$aboutLink["name"]}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td n:if="sizeof($admins) > 0">
|
<td n:if="sizeof($admins) > 0">
|
||||||
<h4>{_administrators}</h4>
|
<h4>{_administrators}</h4>
|
||||||
|
@ -44,14 +53,23 @@
|
||||||
|
|
||||||
{if sizeof($popularClubs) !== 0}
|
{if sizeof($popularClubs) !== 0}
|
||||||
<h4>{_most_popular_groups}</h4>
|
<h4>{_most_popular_groups}</h4>
|
||||||
<ol>
|
{var $entries = array_chunk($popularClubs, 10, true)}
|
||||||
<li n:foreach="$popularClubs as $entry" style="margin-top: 5px;">
|
<table width="100%" cellspacing="0" cellpadding="0">
|
||||||
<a href="{$entry->club->getURL()}">{$entry->club->getName()}</a>
|
<tbody>
|
||||||
<div>
|
<tr valign="top">
|
||||||
{tr("participants", $entry->subscriptions)}
|
<td n:foreach="$entries as $chunk">
|
||||||
</div>
|
<ol>
|
||||||
</li>
|
<li value="{$num+1}" style="margin-top: 5px;" n:foreach="$chunk as $num => $club">
|
||||||
</ol>
|
<a href="{$club->club->getURL()}">{$club->club->getName()}</a>
|
||||||
|
<div>
|
||||||
|
{tr("participants", $club->subscriptions)}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<h4>{_rules}</h4>
|
<h4>{_rules}</h4>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
{presenter "openvk!Support->knowledgeBaseArticle", "about"}
|
||||||
<center>
|
<center>
|
||||||
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_"log_in"}</a>
|
<a class="button" style="margin-right: 5px;cursor: pointer;" href="/login">{_"log_in"}</a>
|
||||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['registration']['enable']" class="button" style="cursor: pointer;" href="/reg">{_"registration"}</a>
|
<a class="button" style="cursor: pointer;" href="/reg">{_"registration"}</a>
|
||||||
</center>
|
</center>
|
||||||
{* TO-DO: Add statistics about this instance as on mastodon.social *}
|
{* TO-DO: Add statistics about this instance as on mastodon.social *}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
{var $instance_name = OPENVK_ROOT_CONF['openvk']['appearance']['name']}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
||||||
<style>
|
<style>
|
||||||
{var css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
{var $css = file_get_contents(OPENVK_ROOT . "/Web/static/js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.css")}
|
||||||
{str_replace("fonts/", "/assets/packages/static/openvk/js/node_modules/@atlassian/aui/dist/aui/fonts/", $css)|noescape}
|
{str_replace("fonts/", "/assets/packages/static/openvk/js/node_modules/@atlassian/aui/dist/aui/fonts/", $css)|noescape}
|
||||||
</style>
|
</style>
|
||||||
<title>{include title} - Админ-панель {=OPENVK_ROOT_CONF['openvk']['appearance']['name']}</title>
|
<title>{include title} - {_admin} {$instance_name}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
|
@ -16,23 +17,15 @@
|
||||||
<div class="aui-header-primary">
|
<div class="aui-header-primary">
|
||||||
<h1 id="logo" class="aui-header-logo aui-header-logo-textonly">
|
<h1 id="logo" class="aui-header-logo aui-header-logo-textonly">
|
||||||
<a href="/admin">
|
<a href="/admin">
|
||||||
<span class="aui-header-logo-device">{=OPENVK_ROOT_CONF['openvk']['appearance']['name']}</span>
|
<span class="aui-header-logo-device">{$instance_name}</span>
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$search ?? false" class="aui-header-secondary">
|
<div n:if="$search ?? false" class="aui-header-secondary">
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<form class="aui-quicksearch dont-default-focus ajs-dirty-warning-exempt">
|
<form class="aui-quicksearch dont-default-focus ajs-dirty-warning-exempt">
|
||||||
<input
|
<input id="quickSearchInput" autocomplete="off" class="search" type="text" placeholder="{include searchTitle}" value="{$_GET['q'] ?? ''}" name="q" accesskey="Q" />
|
||||||
id="quickSearchInput"
|
<input type="hidden" value=1 name=p />
|
||||||
autocomplete="off"
|
|
||||||
class="search"
|
|
||||||
type="text"
|
|
||||||
placeholder="{include searchTitle}"
|
|
||||||
value="{$_GET['q'] ?? ''}"
|
|
||||||
name="q"
|
|
||||||
accesskey="Q" />
|
|
||||||
<input type="hidden" value=1 name=p />
|
|
||||||
</form>
|
</form>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,83 +39,64 @@
|
||||||
<div class="aui-navgroup-inner">
|
<div class="aui-navgroup-inner">
|
||||||
<div class="aui-navgroup-primary">
|
<div class="aui-navgroup-primary">
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Обзор</strong>
|
<strong>{_admin_overview}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin">
|
<a href="/admin">{_admin_overview_summary}</a>
|
||||||
Сводка
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Пользовательский контент</strong>
|
<strong>{_admin_content}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/users">
|
<a href="/admin/users">{_users}</a>
|
||||||
Пользователи
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/clubs">
|
<a href="/admin/clubs">{_groups}</a>
|
||||||
Группы
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Платные услуги</strong>
|
<strong>{_admin_services}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/vouchers">
|
<a href="/admin/vouchers">{_vouchers}</a>
|
||||||
{_vouchers}
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/gifts">
|
<a href="/admin/gifts">{_gifts}</a>
|
||||||
Подарки
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Настройки</strong>
|
<strong>{_admin_settings}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/settings/tuning">
|
<a href="/admin/settings/tuning">{_admin_settings_tuning}</a>
|
||||||
Общие
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/settings/appearance">
|
<a href="/admin/settings/appearance">{_admin_settings_appearance}</a>
|
||||||
Внешний вид
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/settings/security">
|
<a href="/admin/settings/security">{_admin_settings_security}</a>
|
||||||
Безопасность
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/settings/integrations">
|
<a href="/admin/settings/integrations">{_admin_settings_integrations}</a>
|
||||||
Интеграции
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin/settings/system">
|
<a href="/admin/settings/system">{_admin_settings_system}</a>
|
||||||
Система
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="aui-nav-heading">
|
<div class="aui-nav-heading">
|
||||||
<strong>Об OpenVK</strong>
|
<strong>{_admin_about}</strong>
|
||||||
</div>
|
</div>
|
||||||
<ul class="aui-nav">
|
<ul class="aui-nav">
|
||||||
<li>
|
<li>
|
||||||
<a href="/about:openvk">
|
<a href="/about:openvk">{_admin_about_version}</a>
|
||||||
Версия
|
</li>
|
||||||
</a>
|
<li>
|
||||||
|
<a href="/about">{_admin_about_instance}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -131,7 +105,7 @@
|
||||||
</div>
|
</div>
|
||||||
<section class="aui-page-panel-content">
|
<section class="aui-page-panel-content">
|
||||||
{ifset $flashMessage}
|
{ifset $flashMessage}
|
||||||
{var type = ["err" => "error", "warn" => "warning", "info" => "basic", "succ" => "success"][$flashMessage->type]}
|
{var $type = ["err" => "error", "warn" => "warning", "info" => "basic", "succ" => "success"][$flashMessage->type]}
|
||||||
<div class="aui-message aui-message-{$type}" style="margin-bottom: 15px;">
|
<div class="aui-message aui-message-{$type}" style="margin-bottom: 15px;">
|
||||||
<p class="title">
|
<p class="title">
|
||||||
<strong>{$flashMessage->title}</strong>
|
<strong>{$flashMessage->title}</strong>
|
||||||
|
@ -139,11 +113,11 @@
|
||||||
<p>{$flashMessage->msg|noescape}</p>
|
<p>{$flashMessage->msg|noescape}</p>
|
||||||
</div>
|
</div>
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
|
||||||
{ifset preHeader}
|
{ifset preHeader}
|
||||||
{include preHeader}
|
{include preHeader}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
|
||||||
<header class="aui-page-header">
|
<header class="aui-page-header">
|
||||||
<div class="aui-page-header-inner">
|
<div class="aui-page-header-inner">
|
||||||
<div class="aui-page-header-main">
|
<div class="aui-page-header-main">
|
||||||
|
@ -167,11 +141,11 @@
|
||||||
</section>
|
</section>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
{script "js/node_modules/jquery/dist/jquery.min.js"}
|
||||||
{script "js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.js"}
|
{script "js/node_modules/@atlassian/aui/dist/aui/aui-prototyping.js"}
|
||||||
<script>AJS.tabs.setup();</script>
|
<script>AJS.tabs.setup();</script>
|
||||||
|
|
||||||
{ifset scripts}
|
{ifset scripts}
|
||||||
{include scripts}
|
{include scripts}
|
||||||
{/ifset}
|
{/ifset}
|
||||||
|
|
|
@ -1,191 +1,157 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Редактировать {$club->getCanonicalName()}
|
{_edit} {$club->getCanonicalName()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
{$club->getCanonicalName()}
|
{$club->getCanonicalName()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
|
||||||
{var isMain = $mode === 'main'}
|
{var $isMain = $mode === 'main'}
|
||||||
{var isBan = $mode === 'ban'}
|
{var $isBan = $mode === 'ban'}
|
||||||
{var isFollowers = $mode === 'followers'}
|
{var $isFollowers = $mode === 'followers'}
|
||||||
|
|
||||||
{if $isMain}
|
{if $isMain}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
<!-- This main block -->
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
<div class="aui-tabs horizontal-tabs">
|
<div class="aui-navgroup-primary">
|
||||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
<ul class="aui-nav">
|
||||||
<div class="aui-navgroup-inner">
|
<li class="aui-nav-selected"><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
<div class="aui-navgroup-primary">
|
<li><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||||
<ul class="aui-nav">
|
<li><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||||
<li class="aui-nav-selected"><a href="?act=main">Главное</a></li>
|
</ul>
|
||||||
<li><a href="?act=ban">Бан</a></li>
|
</div>
|
||||||
<li><a href="?act=followers">Участники</a></li>
|
</div>
|
||||||
</ul>
|
</nav>
|
||||||
</div>
|
<form class="aui" method="POST">
|
||||||
</div>
|
<div class="field-group">
|
||||||
</nav>
|
<label for="avatar">{_avatar}</label>
|
||||||
<form class="aui" method="POST">
|
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||||
<div class="field-group">
|
|
||||||
<label for="avatar">
|
|
||||||
Аватарка
|
|
||||||
</label>
|
|
||||||
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
|
||||||
<span class="aui-avatar-inner">
|
|
||||||
<img src="{$club->getAvatarUrl()}" style="object-fit: cover;"></img>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="id">
|
|
||||||
ID
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="number" id="id" disabled value="{$club->getId()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="id_owner">
|
|
||||||
ID владельца
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="id_owner" name="id_owner" value="{$club->getOwner()->getId()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="name">
|
|
||||||
Название
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="name" name="name" value="{$club->getName()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="about">
|
|
||||||
Описание
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="about" name="about" value="{$club->getDescription()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="shortcode">
|
|
||||||
Адрес
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$club->getShortCode()}" />
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<div class="group">
|
|
||||||
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $club->isVerified()} checked {/if} />
|
|
||||||
<label for="verify">
|
|
||||||
Верификация
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
|
||||||
<input class="toggle-large" type="checkbox" id="hide_from_global_feed" name="hide_from_global_feed" value="1" {if $club->isHideFromGlobalFeedEnabled()} checked {/if} />
|
|
||||||
<label for="hide_from_global_feed">
|
|
||||||
Не отображать записи в глобальной ленте
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
<div class="buttons-container">
|
|
||||||
<div class="buttons">
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input class="button submit" type="submit" value="Сохранить">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $isBan}
|
|
||||||
|
|
||||||
<!-- This ban block -->
|
|
||||||
|
|
||||||
<div class="aui-tabs horizontal-tabs">
|
|
||||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
|
||||||
<div class="aui-navgroup-inner">
|
|
||||||
<div class="aui-navgroup-primary">
|
|
||||||
<ul class="aui-nav">
|
|
||||||
<li><a href="?act=main">Главное</a></li>
|
|
||||||
<li class="aui-nav-selected"><a href="?act=ban">Бан</a></li>
|
|
||||||
<li><a href="?act=followers">Участники</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<form class="aui" method="POST">
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="ban_reason">
|
|
||||||
Причина бана
|
|
||||||
</label>
|
|
||||||
<input class="text" type="text" id="text-input" name="ban_reason" value="{$club->getBanReason()}" />
|
|
||||||
</div>
|
|
||||||
<hr/>
|
|
||||||
<div class="buttons-container">
|
|
||||||
<div class="buttons">
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input class="button submit" type="submit" value="Сохранить">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{if $isFollowers}
|
|
||||||
|
|
||||||
<!-- This followers block -->
|
|
||||||
|
|
||||||
{var followers = iterator_to_array($followers)}
|
|
||||||
|
|
||||||
<div class="aui-tabs horizontal-tabs">
|
|
||||||
<nav class="aui-navgroup aui-navgroup-horizontal">
|
|
||||||
<div class="aui-navgroup-inner">
|
|
||||||
<div class="aui-navgroup-primary">
|
|
||||||
<ul class="aui-nav">
|
|
||||||
<li><a href="?act=main">Главное</a></li>
|
|
||||||
<li><a href="?act=ban">Бан</a></li>
|
|
||||||
<li class="aui-nav-selected"><a href="?act=followers">Участники</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<table rules="none" class="aui aui-table-list">
|
|
||||||
<tbody>
|
|
||||||
<tr n:foreach="$followers as $follower">
|
|
||||||
<td>{$follower->getId()}</td>
|
|
||||||
<td>
|
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
<img src="{$follower->getAvatarUrl()}" alt="{$follower->getCanonicalName()}" role="presentation" />
|
<img src="{$club->getAvatarUrl('tiny')}" style="object-fit: cover;"></img>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
<a href="{$follower->getURL()}">{$follower->getCanonicalName()}</a>
|
<div class="field-group">
|
||||||
|
<label for="id">ID</label>
|
||||||
<span n:if="$follower->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
<input class="text medium-field" type="number" id="id" disabled value="{$club->getId()}" />
|
||||||
заблокирован
|
</div>
|
||||||
</span>
|
<div class="field-group">
|
||||||
</td>
|
<label for="id_owner">{_admin_ownerid}</label>
|
||||||
<td>{$follower->isFemale() ? "Женский" : "Мужской"}</td>
|
<input class="text medium-field" type="text" id="id_owner" name="id_owner" value="{$club->getOwner()->getId()}" />
|
||||||
<td>{$follower->getShortCode() ?? "(отсутствует)"}</td>
|
</div>
|
||||||
<td>{$follower->getRegistrationTime()}</td>
|
<div class="field-group">
|
||||||
<td>
|
<label for="name">{_admin_title}</label>
|
||||||
<a class="aui-button aui-button-primary" href="/admin/users/id{$follower->getId()}">
|
<input class="text medium-field" type="text" id="name" name="name" value="{$club->getName()}" />
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
</div>
|
||||||
</a>
|
<div class="field-group">
|
||||||
</td>
|
<label for="about">{_admin_description}</label>
|
||||||
</tr>
|
<input class="text medium-field" type="text" id="about" name="about" value="{$club->getDescription()}" />
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
<div class="field-group">
|
||||||
<div align="right">
|
<label for="shortcode">{_admin_shortcode}</label>
|
||||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$club->getShortCode()}" />
|
||||||
|
</div>
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
<br/>
|
||||||
⭁ туда
|
<div class="group">
|
||||||
</a>
|
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $club->isVerified()} checked {/if} />
|
||||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
<label for="verify">{_admin_verification}</label>
|
||||||
⭇ сюда
|
</div>
|
||||||
</a>
|
<div class="group">
|
||||||
</div>
|
<input class="toggle-large" type="checkbox" id="hide_from_global_feed" name="hide_from_global_feed" value="1" {if $club->isHideFromGlobalFeedEnabled()} checked {/if} />
|
||||||
</div>
|
<label for="hide_from_global_feed">{_admin_club_excludeglobalfeed}</label>
|
||||||
{/if}
|
</div>
|
||||||
{/block}
|
<hr/>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $isBan}
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
|
<li class="aui-nav-selected"><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||||
|
<li><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<form class="aui" method="POST">
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="ban_reason">{_admin_banreason}</label>
|
||||||
|
<input class="text" type="text" id="text-input" name="ban_reason" value="{$club->getBanReason()}" />
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{if $isFollowers}
|
||||||
|
{var $followers = iterator_to_array($followers)}
|
||||||
|
|
||||||
|
<div class="aui-tabs horizontal-tabs">
|
||||||
|
<nav class="aui-navgroup aui-navgroup-horizontal">
|
||||||
|
<div class="aui-navgroup-inner">
|
||||||
|
<div class="aui-navgroup-primary">
|
||||||
|
<ul class="aui-nav">
|
||||||
|
<li><a href="?act=main">{_admin_tab_main}</a></li>
|
||||||
|
<li><a href="?act=ban">{_admin_tab_ban}</a></li>
|
||||||
|
<li class="aui-nav-selected"><a href="?act=followers">{_admin_tab_followers}</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<table rules="none" class="aui aui-table-list">
|
||||||
|
<tbody>
|
||||||
|
<tr n:foreach="$followers as $follower">
|
||||||
|
<td>{$follower->getId()}</td>
|
||||||
|
<td>
|
||||||
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
|
<span class="aui-avatar-inner">
|
||||||
|
<img src="{$follower->getAvatarUrl()}" alt="{$follower->getCanonicalName()}" role="presentation" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a href="{$follower->getURL()}">{$follower->getCanonicalName()}</a>
|
||||||
|
|
||||||
|
<span n:if="$follower->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
|
</td>
|
||||||
|
<td>{$follower->isFemale() ? tr("female") : tr("male")}</td>
|
||||||
|
<td>{$follower->getShortCode() ?? "(" . tr("none") . ")"}</td>
|
||||||
|
<td>{$follower->getRegistrationTime()}</td>
|
||||||
|
<td>
|
||||||
|
<a class="aui-button aui-button-primary" href="/admin/users/id{$follower->getId()}">
|
||||||
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div align="right">
|
||||||
|
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||||
|
|
||||||
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
{var search = true}
|
{var $search = true}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Группы
|
{_admin_club_search}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
Бутылки
|
{_groups}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block searchTitle}Поиск бутылок{/block}
|
{block searchTitle}
|
||||||
|
{include title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
{var clubs = iterator_to_array($clubs)}
|
{var $clubs = iterator_to_array($clubs)}
|
||||||
{var amount = sizeof($clubs)}
|
{var $amount = sizeof($clubs)}
|
||||||
|
|
||||||
<table class="aui aui-table-list">
|
<table class="aui aui-table-list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>ID</th>
|
||||||
<th>Имя</th>
|
<th>{_admin_title}</th>
|
||||||
<th>Автор</th>
|
<th>{_admin_author}</th>
|
||||||
<th>Описание</th>
|
<th>{_admin_description}</th>
|
||||||
<th>Короткий адрес</th>
|
<th>{_admin_shortcode}</th>
|
||||||
<th>Действия</th>
|
<th>{_admin_actions}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -32,28 +34,28 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
<img src="{$club->getAvatarUrl()}" alt="{$club->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
<img src="{$club->getAvatarUrl('miniscule')}" alt="{$club->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
<a href="{$club->getURL()}">{$club->getCanonicalName()}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{var user = $club->getOwner()}
|
{var $user = $club->getOwner()}
|
||||||
|
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
<img src="{$user->getAvatarUrl()}" alt="{$user->getCanonicalName()}" role="presentation" />
|
<img src="{$user->getAvatarUrl('miniscule')}" alt="{$user->getCanonicalName()}" role="presentation" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{$club->getDescription() ?? "(не указано)"}</td>
|
<td>{$club->getDescription() ?? "(" . tr("none") . ")"}</td>
|
||||||
<td>{$club->getShortCode()}</td>
|
<td>{$club->getShortCode()}</td>
|
||||||
<td>
|
<td>
|
||||||
<a class="aui-button aui-button-primary" href="/admin/clubs/id{$club->getId()}">
|
<a class="aui-button aui-button-primary" href="/admin/clubs/id{$club->getId()}">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -61,13 +63,9 @@
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
<div align="right">
|
<div align="right">
|
||||||
{var isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
{var $isLast = ((10 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||||
|
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
⭁ туда
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
</a>
|
|
||||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
|
||||||
⭇ сюда
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
{if $form->id === 0}
|
{if $form->id === 0}
|
||||||
Новый подарок
|
{_admin_newgift}
|
||||||
{else}
|
{else}
|
||||||
Подарок "{$form->name}"
|
{_gift} "{$form->name}"
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<form class="aui" method="POST" enctype="multipart/form-data">
|
<form class="aui" method="POST" enctype="multipart/form-data">
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="avatar">
|
<label for="avatar">
|
||||||
Изображение
|
{_admin_image}
|
||||||
<span n:if="$form->id === 0" class="aui-icon icon-required"></span>
|
<span n:if="$form->id === 0" class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
{if $form->id === 0}
|
{if $form->id === 0}
|
||||||
|
@ -29,43 +29,39 @@
|
||||||
</span>
|
</span>
|
||||||
<input style="display: none;" id="picInput" type="file" name="pic" accept="image/jpeg,image/png,image/gif,image/webp" />
|
<input style="display: none;" id="picInput" type="file" name="pic" accept="image/jpeg,image/png,image/gif,image/webp" />
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<a id="picChange" href="javascript:false">Заменить изображение?</a>
|
<a id="picChange" href="javascript:false">{_admin_image_replace}</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="id">
|
<label for="id">ID</label>
|
||||||
ID
|
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="number" id="id" disabled="disabled" value="{$form->id}" />
|
<input class="text long-field" type="number" id="id" disabled="disabled" value="{$form->id}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="putin">
|
<label for="usages">{_admin_uses}</label>
|
||||||
Использований
|
<input class="text long-field" type="number" id="usages" disabled="disabled" value="{$form->usages}" />
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="number" id="putin" disabled="disabled" value="{$form->usages}" />
|
|
||||||
<div n:if="$form->usages > 0" class="description">
|
<div n:if="$form->usages > 0" class="description">
|
||||||
<a href="javascript:$('#putin').value(0);">Обнулить?</a>
|
<a href="javascript:$('#usages').value(0);">{_admin_uses_reset}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="name">
|
<label for="name">
|
||||||
Внутренее имя
|
{_admin_name}
|
||||||
<span class="aui-icon icon-required"></span>
|
<span class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="text" id="name" name="name" value="{$form->name}" />
|
<input class="text long-field" type="text" id="name" name="name" value="{$form->name}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="price">
|
<label for="price">
|
||||||
Цена
|
{_admin_price}
|
||||||
<span class="aui-icon icon-required"></span>
|
<span class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="number" id="price" name="price" min="0" value="{$form->price}" />
|
<input class="text long-field" type="number" id="price" name="price" min="0" value="{$form->price}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="limit">
|
<label for="limit">
|
||||||
Ограничение
|
{_admin_limits}
|
||||||
<span class="aui-icon icon-required"></span>
|
<span class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="number" min="-1" id="limit" name="limit" value="{$form->limit}" />
|
<input class="text long-field" type="number" min="-1" id="limit" name="limit" value="{$form->limit}" />
|
||||||
|
@ -75,13 +71,13 @@
|
||||||
<div class="checkbox" resolved="">
|
<div class="checkbox" resolved="">
|
||||||
<input n:attr="disabled => $form->id === 0, checked => $form->id === 0" class="checkbox" type="checkbox" name="reset_limit" id="reset_limit" />
|
<input n:attr="disabled => $form->id === 0, checked => $form->id === 0" class="checkbox" type="checkbox" name="reset_limit" id="reset_limit" />
|
||||||
<span class="aui-form-glyph"></span>
|
<span class="aui-form-glyph"></span>
|
||||||
|
|
||||||
<label for="reset_limit">Сбросить счётчик ограничений</label>
|
<label for="reset_limit">{_admin_limits_reset}</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<input n:if="$form->id === 0" type="hidden" name="_cat" value="{$_GET['cat'] ?? 1}" />
|
<input n:if="$form->id === 0" type="hidden" name="_cat" value="{$_GET['cat'] ?? 1}" />
|
||||||
|
|
||||||
<div class="buttons-container">
|
<div class="buttons-container">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
@ -94,12 +90,12 @@
|
||||||
{block scripts}
|
{block scripts}
|
||||||
<script>
|
<script>
|
||||||
const TRANS_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
const TRANS_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
|
||||||
|
|
||||||
$("#picChange").click(_ => $("#picInput").click());
|
$("#picChange").click(_ => $("#picInput").click());
|
||||||
$("#picInput").bind("change", e => {
|
$("#picInput").bind("change", e => {
|
||||||
if(typeof e.target.files[0] === "undefined")
|
if(typeof e.target.files[0] === "undefined")
|
||||||
$("#pic").prop("src", URL.createObjectURL(TRANS_GIF));
|
$("#pic").prop("src", URL.createObjectURL(TRANS_GIF));
|
||||||
|
|
||||||
$("#pic").prop("src", URL.createObjectURL(e.target.files[0]));
|
$("#pic").prop("src", URL.createObjectURL(e.target.files[0]));
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Наборы подарков
|
{_admin_giftsets}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block headingWrap}
|
{block headingWrap}
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
{_create}
|
{_create}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1>Наборы подарков</h1>
|
<h1>{_admin_giftsets}</h1>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
@ -27,12 +27,11 @@
|
||||||
</td>
|
</td>
|
||||||
<td style="vertical-align: middle; text-align: right;">
|
<td style="vertical-align: middle; text-align: right;">
|
||||||
<a class="aui-button aui-button-primary" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}.meta">
|
<a class="aui-button aui-button-primary" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}.meta">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="aui-button" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}/">
|
<a class="aui-button" href="/admin/gifts/{$cat->getSlug()}.{$cat->getId()}/">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-gallery">Открыть</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-gallery">{_admin_open}</span>
|
||||||
Открыть
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -40,17 +39,13 @@
|
||||||
</table>
|
</table>
|
||||||
{else}
|
{else}
|
||||||
<center>
|
<center>
|
||||||
<p>Наборов подарков нету. Чтобы создать подарок, создайте набор.</p>
|
<p>{_admin_giftsets_none}</p>
|
||||||
</center>
|
</center>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div align="right">
|
<div align="right">
|
||||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
|
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($categories)) < $count}
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) - 1}">
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
⭁ туда
|
<a n:if="$isLast" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
</a>
|
|
||||||
<a n:if="$isLast" class="aui-button" href="?act={$act}&p={($_GET['p'] ?? 1) + 1}">
|
|
||||||
⭇ сюда
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
{if $form->id === 0}
|
{if $form->id === 0}
|
||||||
Создать набор подарков
|
{_admin_giftsets_create}
|
||||||
{else}
|
{else}
|
||||||
{$form->languages["master"]->name}
|
{$form->languages["master"]->name}
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<form class="aui" method="POST">
|
<form class="aui" method="POST">
|
||||||
<h3>Общие настройки</h3>
|
<h3>{_admin_commonsettings}</h3>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="id">
|
<label for="id">
|
||||||
|
@ -24,37 +24,37 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="name_master">
|
<label for="name_master">
|
||||||
Наименование
|
{_admin_name}
|
||||||
<span class="aui-icon icon-required"></span>
|
<span class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="text" id="name_master" name="name_master" value="{$form->languages['master']->name}" />
|
<input class="text long-field" type="text" id="name_master" name="name_master" value="{$form->languages['master']->name}" />
|
||||||
<div class="description">Внутреннее название набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
<div class="description">{_admin_giftsets_title}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="description_master">
|
<label for="description_master">
|
||||||
Описание
|
{_admin_description}
|
||||||
<span class="aui-icon icon-required"></span>
|
<span class="aui-icon icon-required"></span>
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="text" id="description_master" name="description_master" value="{$form->languages['master']->description}" />
|
<input class="text long-field" type="text" id="description_master" name="description_master" value="{$form->languages['master']->description}" />
|
||||||
<div class="description">Внутреннее описание набора, которое будет использоваться, если не удаётся найти название на языке пользователя.</div>
|
<div class="description">{_admin_giftsets_description}</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<h3>Языко-зависимые настройки</h3>
|
<h3>{_admin_langsettings}</h3>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
{foreach $form->languages as $locale => $data}
|
{foreach $form->languages as $locale => $data}
|
||||||
{continueIf $locale === "master"}
|
{continueIf $locale === "master"}
|
||||||
|
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="name_{$locale}">
|
<label for="name_{$locale}">
|
||||||
Наименование
|
{_admin_name}
|
||||||
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="text" id="name_{$locale}" name="name_{$locale}" value="{$data->name}" />
|
<input class="text long-field" type="text" id="name_{$locale}" name="name_{$locale}" value="{$data->name}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="description_{$locale}">
|
<label for="description_{$locale}">
|
||||||
Описание
|
{_admin_description}
|
||||||
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
<img src="/assets/packages/static/openvk/img/flags/{$locale}.gif" alt="{$locale}" />
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="text" id="description_{$locale}" name="description_{$locale}" value="{$data->description}" />
|
<input class="text long-field" type="text" id="description_{$locale}" name="description_{$locale}" value="{$data->description}" />
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{_create}
|
{_create}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1>Набор "{$cat->getName()}"</h1>
|
<h1>{_admin_giftset} "{$cat->getName()}"</h1>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
@ -32,11 +32,11 @@
|
||||||
<td style="vertical-align: middle;">
|
<td style="vertical-align: middle;">
|
||||||
{$gift->getName()}
|
{$gift->getName()}
|
||||||
<span n:if="$gift->isFree()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">
|
<span n:if="$gift->isFree()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">
|
||||||
бесплатный
|
{_admin_price_free}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="vertical-align: middle;">
|
<td style="vertical-align: middle;">
|
||||||
{$gift->getPrice()} голосов
|
{tr("points_amount", $gift->getPrice())}
|
||||||
</td>
|
</td>
|
||||||
<td style="vertical-align: middle;">
|
<td style="vertical-align: middle;">
|
||||||
{$gift->getUsages()} раз
|
{$gift->getUsages()} раз
|
||||||
|
@ -71,12 +71,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div align="right">
|
<div align="right">
|
||||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
|
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($gifts)) < $count}
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
|
||||||
⭁ туда
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
</a>
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
|
||||||
⭇ сюда
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Сводка
|
{_admin_overview_summary}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
Сводка
|
{_admin_overview_summary}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
Да!
|
┬─┬︵/(.□.)╯
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Редактировать {$user->getCanonicalName()}
|
{_edit} {$user->getCanonicalName()}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
|
@ -10,89 +10,70 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div class="aui-tabs horizontal-tabs">
|
<div class="aui-tabs horizontal-tabs">
|
||||||
<form class="aui" method="POST">
|
<form class="aui" method="POST">
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="avatar">
|
<label for="avatar">{_avatar}</label>
|
||||||
Аватарка
|
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
||||||
</label>
|
<span class="aui-avatar-inner">
|
||||||
<span id="avatar" class="aui-avatar aui-avatar-project aui-avatar-xlarge">
|
<img src="{$user->getAvatarUrl('tiny')}" style="object-fit: cover;"></img>
|
||||||
<span class="aui-avatar-inner">
|
</span>
|
||||||
<img src="{$user->getAvatarUrl()}" style="object-fit: cover;"></img>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="id">
|
|
||||||
ID
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="number" id="id" disabled value="{$user->getId()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="guid">
|
|
||||||
GUID
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" id="guid" disabled value="{$user->getChandlerUser()->getId()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="first_name">
|
|
||||||
Имя
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="first_name" name="first_name" value="{$user->getFirstName()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="last_name">
|
|
||||||
Фамилия
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="last_name" name="last_name" value="{$user->getLastName()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="nickname">
|
|
||||||
Никнейм
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="nickname" name="nickname" value="{$user->getPseudo()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="status">
|
|
||||||
Статус
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="status" name="status" value="{$user->getStatus()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="email">
|
|
||||||
E-Mail
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="email" id="email" name="email" value="{$user->getEmail()}" />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="shortcode">
|
|
||||||
Адрес страницы
|
|
||||||
</label>
|
|
||||||
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$user->getShortCode()}" />
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="city">
|
|
||||||
Верификация
|
|
||||||
</label>
|
|
||||||
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $user->isVerified()} checked {/if} />
|
|
||||||
</div>
|
|
||||||
<div class="field-group">
|
|
||||||
<label for="city">
|
|
||||||
Онлайн статус
|
|
||||||
</label>
|
|
||||||
<select name="online" class="select">
|
|
||||||
<option value="0" {if $user->onlineStatus() > 2}selected{/if}>По-умолчанию</option>
|
|
||||||
<option value="1" {if $user->onlineStatus() == 1}selected{/if}>Инкогнито</option>
|
|
||||||
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>Юзер умер</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="buttons-container">
|
|
||||||
<div class="buttons">
|
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
|
||||||
<input class="button submit" type="submit" value="Сохранить">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="field-group">
|
||||||
</form>
|
<label for="id">ID</label>
|
||||||
|
<input class="text medium-field" type="number" id="id" disabled value="{$user->getId()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="guid">GUID</label>
|
||||||
|
<input class="text medium-field" id="guid" disabled value="{$user->getChandlerUser()->getId()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="registration_ip">{_admin_first_known_ip}</label>
|
||||||
|
<input class="text medium-field" id="guid" disabled value="{$user->getRegistrationIP()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="first_name">{_name}</label>
|
||||||
|
<input class="text medium-field" type="text" id="first_name" name="first_name" value="{$user->getFirstName()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="last_name">{_surname}</label>
|
||||||
|
<input class="text medium-field" type="text" id="last_name" name="last_name" value="{$user->getLastName()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="nickname">{_nickname}</label>
|
||||||
|
<input class="text medium-field" type="text" id="nickname" name="nickname" value="{$user->getPseudo()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="status">{_status}</label>
|
||||||
|
<input class="text medium-field" type="text" id="status" name="status" value="{$user->getStatus()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="email">E-Mail</label>
|
||||||
|
<input class="text medium-field" type="email" id="email" name="email" value="{$user->getEmail()}" />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="shortcode">{_admin_shortcode}</label>
|
||||||
|
<input class="text medium-field" type="text" id="shortcode" name="shortcode" value="{$user->getShortCode()}" />
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="city">{_admin_verification}</label>
|
||||||
|
<input class="toggle-large" type="checkbox" id="verify" name="verify" value="1" {if $user->isVerified()} checked {/if} />
|
||||||
|
</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<label for="city">{_admin_user_online}</label>
|
||||||
|
<select name="online" class="select">
|
||||||
|
<option value="0" {if $user->onlineStatus() > 2}selected{/if}>{_admin_user_online_default}</option>
|
||||||
|
<option value="1" {if $user->onlineStatus() == 1}selected{/if}>{_admin_user_online_incognite}</option>
|
||||||
|
<option value="2" {if $user->onlineStatus() == 2}selected{/if}>{_admin_user_online_deceased}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="buttons-container">
|
||||||
|
<div class="buttons">
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input class="button submit" type="submit" value="{_save}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
{extends "@layout.xml"}
|
{extends "@layout.xml"}
|
||||||
{var search = true}
|
{var $search = true}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
Пользователи
|
{_admin_user_search}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
Пиздюки
|
{_users}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block searchTitle}Поиск пиздюков{/block}
|
{block searchTitle}
|
||||||
|
{include title}
|
||||||
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
{var users = iterator_to_array($users)}
|
{var $users = iterator_to_array($users)}
|
||||||
{var amount = sizeof($users)}
|
{var $amount = sizeof($users)}
|
||||||
|
|
||||||
<table class="aui aui-table-list">
|
<table class="aui aui-table-list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>ID</th>
|
||||||
<th>Имя</th>
|
<th>{_admin_name}</th>
|
||||||
<th>Пол</th>
|
<th>{_gender}</th>
|
||||||
<th>Короткий адрес</th>
|
<th>{_admin_shortcode}</th>
|
||||||
<th>Дата регистрации</th>
|
<th>{_registration_date}</th>
|
||||||
<th>Действия</th>
|
<th>{_admin_actions}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -32,26 +34,24 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
<img src="{$user->getAvatarUrl()}" alt="{$user->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
<img src="{$user->getAvatarUrl('miniscule')}" alt="{$user->getCanonicalName()}" style="object-fit: cover;" role="presentation" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||||
|
|
||||||
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
заблокирован
|
|
||||||
</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td>{$user->isFemale() ? "Женский" : "Мужской"}</td>
|
<td>{$user->isFemale() ? tr("female") : tr("male")}</td>
|
||||||
<td>{$user->getShortCode() ?? "(отсутствует)"}</td>
|
<td>{$user->getShortCode() ?? "(" . tr("none") . ")"}</td>
|
||||||
<td>{$user->getRegistrationTime()}</td>
|
<td>{$user->getRegistrationTime()}</td>
|
||||||
<td>
|
<td>
|
||||||
<a class="aui-button aui-button-primary" href="/admin/users/id{$user->getId()}">
|
<a class="aui-button aui-button-primary" href="/admin/users/id{$user->getId()}">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
</a>
|
</a>
|
||||||
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
|
{if $thisUser->getChandlerUser()->can("substitute")->model('openvk\Web\Models\Entities\User')->whichBelongsTo(0)}
|
||||||
<a class="aui-button" href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}">
|
<a class="aui-button" href="/setSID/{$user->getChandlerUser()->getId()}?hash={rawurlencode($csrfToken)}">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">Войти как</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-sign-in">{_admin_loginas}</span>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
@ -60,13 +60,9 @@
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
<div align="right">
|
<div align="right">
|
||||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + $amount) < $count}
|
||||||
|
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
⭁ туда
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
</a>
|
|
||||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
|
||||||
⭇ сюда
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -5,45 +5,36 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block heading}
|
{block heading}
|
||||||
{_edit} №{$form->token ?? "undefined"}
|
{_edit} #{$form->token ?? "undefined"}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
|
<div style="margin: 8px -8px;" class="aui-tabs horizontal-tabs">
|
||||||
<ul class="tabs-menu">
|
<ul class="tabs-menu">
|
||||||
<li class="menu-item active-tab">
|
<li class="menu-item active-tab">
|
||||||
<a href="#info">Информация</a>
|
<a href="#info">{_admin_tab_main}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="menu-item">
|
<li class="menu-item">
|
||||||
<a href="#activators">{_voucher_activators}</a>
|
<a href="#activators">{_voucher_activators}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tabs-pane active-pane" id="info">
|
<div class="tabs-pane active-pane" id="info">
|
||||||
<form class="aui" method="POST">
|
<form class="aui" method="POST">
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="id">
|
<label for="id">ID</label>
|
||||||
ID
|
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
<input class="text long-field" type="number" id="id" name="id" disabled value="{$form->id}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="token">
|
<label for="token">{_admin_voucher_serial}</label>
|
||||||
Серийный номер
|
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="text" id="token" name="token" value="{$form->token}" />
|
<input class="text long-field" type="text" id="token" name="token" value="{$form->token}" />
|
||||||
<div class="description">Номер состоит из 24 символов, если формат неправильный или поле не заполнено, будет назначен автоматически.</div>
|
<div class="description">{_admin_voucher_serial_desc}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="coins">
|
<label for="coins">{_admin_voucher_coins}</label>
|
||||||
Количество голосов
|
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="number" min="0" id="coins" name="coins" value="{$form->coins}" />
|
<input class="text long-field" type="number" min="0" id="coins" name="coins" value="{$form->coins}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label for="rating">
|
<label for="rating">{_admin_voucher_rating}</label>
|
||||||
Количество рейтинга
|
|
||||||
</label>
|
|
||||||
<input class="text long-field" type="number" min="0" id="rating" name="rating" value="{$form->rating}" />
|
<input class="text long-field" type="number" min="0" id="rating" name="rating" value="{$form->rating}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
|
@ -55,9 +46,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
</label>
|
</label>
|
||||||
<input class="text long-field" type="number" min="-1" id="usages" name="usages" value="{$form->usages}" />
|
<input class="text long-field" type="number" min="-1" id="usages" name="usages" value="{$form->usages}" />
|
||||||
<div class="description">Количество аккаунтов, которые могут использовать ваучер. Если написать -1, будет Infinity.</div>
|
<div class="description">{_admin_voucher_usages_desc}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="buttons-container">
|
<div class="buttons-container">
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
@ -66,7 +57,6 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tabs-pane" id="activators">
|
<div class="tabs-pane" id="activators">
|
||||||
<table rules="none" class="aui aui-table-list">
|
<table rules="none" class="aui aui-table-list">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -74,15 +64,13 @@
|
||||||
<td>
|
<td>
|
||||||
<span class="aui-avatar aui-avatar-xsmall">
|
<span class="aui-avatar aui-avatar-xsmall">
|
||||||
<span class="aui-avatar-inner">
|
<span class="aui-avatar-inner">
|
||||||
<img src="{$user->getAvatarUrl()}" alt="{$user->getCanonicalName()}" role="presentation" />
|
<img src="{$user->getAvatarUrl('miniscule')}" alt="{$user->getCanonicalName()}" role="presentation" />
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a>
|
||||||
|
|
||||||
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">
|
<span n:if="$user->isBanned()" class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_banned}</span>
|
||||||
заблокирован
|
|
||||||
</span>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/vouchers/id0">
|
<a style="float: right;" class="aui-button aui-button-primary" href="/admin/vouchers/id0">
|
||||||
{_create}
|
{_create}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1>{_vouchers}</h1>
|
<h1>{_vouchers}</h1>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@
|
||||||
<table class="aui aui-table-list">
|
<table class="aui aui-table-list">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>ID</th>
|
||||||
<th>Серийный номер</th>
|
<th>{_admin_voucher_serial}</th>
|
||||||
<th>Голоса</th>
|
<th>{_coins}</th>
|
||||||
<th>Рейгтинг</th>
|
<th>{_admin_voucher_rating}</th>
|
||||||
<th>Осталось использований</th>
|
<th>{_usages_left}</th>
|
||||||
<th>Состояние</th>
|
<th>{_admin_voucher_status}</th>
|
||||||
<th>Действия</th>
|
<th>{_admin_actions}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -34,28 +34,25 @@
|
||||||
<td>{$voucher->getRemainingUsages() === INF ? "∞" : $voucher->getRemainingUsages()}</td>
|
<td>{$voucher->getRemainingUsages() === INF ? "∞" : $voucher->getRemainingUsages()}</td>
|
||||||
<td>
|
<td>
|
||||||
{if $voucher->isExpired()}
|
{if $voucher->isExpired()}
|
||||||
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">закончился</span>
|
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-removed">{_admin_voucher_status_closed}</span>
|
||||||
{else}
|
{else}
|
||||||
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">активен</span>
|
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">{_admin_voucher_status_opened}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a class="aui-button aui-button-primary" href="/admin/vouchers/id{$voucher->getId()}">
|
<a class="aui-button aui-button-primary" href="/admin/vouchers/id{$voucher->getId()}">
|
||||||
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">Редактировать</span>
|
<span class="aui-icon aui-icon-small aui-iconfont-new-edit">{_edit}</span>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<div align="right">
|
<div align="right">
|
||||||
{var isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count}
|
{var $isLast = ((20 * (($_GET['p'] ?? 1) - 1)) + sizeof($vouchers)) < $count}
|
||||||
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">
|
|
||||||
⭁ туда
|
<a n:if="($_GET['p'] ?? 1) > 1" class="aui-button" href="?p={($_GET['p'] ?? 1) - 1}">«</a>
|
||||||
</a>
|
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">»</a>
|
||||||
<a n:if="$isLast" class="aui-button" href="?p={($_GET['p'] ?? 1) + 1}">
|
|
||||||
⭇ сюда
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<span>{_code}: </span>
|
<span>{_code}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" name="code" required />
|
<input type="text" name="code" autocomplete="off" required />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
<span>{_"gender"}: </span>
|
<span>{_"gender"}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{var femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
|
{var $femalePreferred = OPENVK_ROOT_CONF["openvk"]["preferences"]["femaleGenderPriority"]}
|
||||||
<select name="sex" required>
|
<select name="sex" required>
|
||||||
<option n:attr="selected => !$femalePreferred" value="male">{_"male"}</option>
|
<option n:attr="selected => !$femalePreferred" value="male">{_"male"}</option>
|
||||||
<option n:attr="selected => $femalePreferred" value="female">{_"female"}</option>
|
<option n:attr="selected => $femalePreferred" value="female">{_"female"}</option>
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
<span class="nobold">{_group_administrators_list}: </span>
|
<span class="nobold">{_group_administrators_list}: </span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{var areAllAdminsHidden = $club->getManagersCount(true) == 0}
|
{var $areAllAdminsHidden = $club->getManagersCount(true) == 0}
|
||||||
<input type="radio" name="administrators_list_display" value="0" n:attr="checked => $club->getAdministratorsListDisplay() == 0, disabled => $areAllAdminsHidden" /> {_group_display_only_creator}<br>
|
<input type="radio" name="administrators_list_display" value="0" n:attr="checked => $club->getAdministratorsListDisplay() == 0, disabled => $areAllAdminsHidden" /> {_group_display_only_creator}<br>
|
||||||
<input type="radio" name="administrators_list_display" value="1" n:attr="checked => $club->getAdministratorsListDisplay() == 1, disabled => $areAllAdminsHidden" /> {_group_display_all_administrators}<br>
|
<input type="radio" name="administrators_list_display" value="1" n:attr="checked => $club->getAdministratorsListDisplay() == 1, disabled => $areAllAdminsHidden" /> {_group_display_all_administrators}<br>
|
||||||
<input type="radio" name="administrators_list_display" value="2" n:attr="checked => $club->getAdministratorsListDisplay() == 2" /> {_group_dont_display_administrators_list}<br>
|
<input type="radio" name="administrators_list_display" value="2" n:attr="checked => $club->getAdministratorsListDisplay() == 2" /> {_group_dont_display_administrators_list}<br>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var $Manager = openvk\Web\Models\Entities\Manager::class}
|
{var $Manager = openvk\Web\Models\Entities\Manager::class}
|
||||||
{var iterator = $onlyShowManagers ? $managers : $followers}
|
{var $iterator = $onlyShowManagers ? $managers : $followers}
|
||||||
{var count = $paginatorConf->count}
|
{var $count = $paginatorConf->count}
|
||||||
{var page = $paginatorConf->page}
|
{var $page = $paginatorConf->page}
|
||||||
{var perPage = 6}
|
{var $perPage = 6}
|
||||||
|
|
||||||
{block title}{_followers} {$club->getCanonicalName()}{/block}
|
{block title}{_followers} {$club->getCanonicalName()}{/block}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x instanceof $Manager ? $x->getUser()->getAvatarURL() : $x->getAvatarURL()}" alt="{$x instanceof $Manager ? $x->getUser()->getCanonicalName() : $x->getCanonicalName()}" width=75 />
|
<img src="{$x instanceof $Manager ? $x->getUser()->getAvatarURL() : $x->getAvatarURL('miniscule')}" alt="{$x instanceof $Manager ? $x->getUser()->getCanonicalName() : $x->getCanonicalName()}" width=75 />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
|
@ -49,8 +49,8 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block description}
|
{block description}
|
||||||
{var user = $x instanceof $Manager ? $x->getUser() : $x}
|
{var $user = $x instanceof $Manager ? $x->getUser() : $x}
|
||||||
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
{var $manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -106,8 +106,8 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block actions}
|
{block actions}
|
||||||
{var user = $x instanceof $Manager ? $x->getUser() : $x}
|
{var $user = $x instanceof $Manager ? $x->getUser() : $x}
|
||||||
{var manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
{var $manager = $x instanceof $Manager ? $x : $club->getManager($user, !$club->canBeModifiedBy($thisUser))}
|
||||||
{if $club->canBeModifiedBy($thisUser ?? NULL)}
|
{if $club->canBeModifiedBy($thisUser ?? NULL)}
|
||||||
<a class="profile_link" href="/club{$club->getId()}/setAdmin?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
|
<a class="profile_link" href="/club{$club->getId()}/setAdmin?user={$user->getId()}&hash={rawurlencode($csrfToken)}" n:if="$club->getOwner()->getId() !== $user->getId()">
|
||||||
{if $manager}
|
{if $manager}
|
||||||
|
@ -140,4 +140,4 @@
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<div class="left_big_block">
|
<div class="left_big_block">
|
||||||
|
<div n:if="!is_null($alert = $club->getAlert())" class="group-alert">{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}</div>
|
||||||
|
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this);">
|
<div class="content_title_expanded" onclick="hidePanel(this);">
|
||||||
{_"information"}
|
{_"information"}
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,7 +43,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$club->getFollowersCount() > 0">
|
<div n:if="$club->getFollowersCount() > 0">
|
||||||
{var followersCount = $club->getFollowersCount()}
|
{var $followersCount = $club->getFollowersCount()}
|
||||||
|
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$followersCount});">
|
||||||
{_participants}
|
{_participants}
|
||||||
|
@ -57,7 +59,7 @@
|
||||||
<div class="cl_element" n:foreach="$club->getFollowers(1) as $follower">
|
<div class="cl_element" n:foreach="$club->getFollowers(1) as $follower">
|
||||||
<div class="cl_avatar">
|
<div class="cl_avatar">
|
||||||
<a href="{$follower->getURL()}">
|
<a href="{$follower->getURL()}">
|
||||||
<img class="ava" src="{$follower->getAvatarUrl()}" />
|
<img class="ava" src="{$follower->getAvatarUrl('miniscule')}" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="{$follower->getURL()}" class="cl_name">
|
<a href="{$follower->getURL()}" class="cl_name">
|
||||||
|
@ -91,10 +93,10 @@
|
||||||
{presenter "openvk!Wall->wallEmbedded", -$club->getId()}
|
{presenter "openvk!Wall->wallEmbedded", -$club->getId()}
|
||||||
</div>
|
</div>
|
||||||
<div class="right_small_block">
|
<div class="right_small_block">
|
||||||
{var avatarPhoto = $club->getAvatarPhoto()}
|
{var $avatarPhoto = $club->getAvatarPhoto()}
|
||||||
{var avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
|
{var $avatarLink = ((is_null($avatarPhoto) ? FALSE : $avatarPhoto->isAnonymous()) ? "/photo" . ("s/" . base_convert((string) $avatarPhoto->getId(), 10, 32)) : $club->getAvatarLink())}
|
||||||
<a href="{$avatarLink|nocheck}">
|
<a href="{$avatarLink|nocheck}">
|
||||||
<img src="{$club->getAvatarUrl()}" style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
<img src="{$club->getAvatarUrl('normal')}" style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
||||||
</a>
|
</a>
|
||||||
<div n:ifset="$thisUser" id="profile_links">
|
<div n:ifset="$thisUser" id="profile_links">
|
||||||
{if $club->canBeModifiedBy($thisUser)}
|
{if $club->canBeModifiedBy($thisUser)}
|
||||||
|
@ -132,7 +134,7 @@
|
||||||
{_"creator"}
|
{_"creator"}
|
||||||
</div>
|
</div>
|
||||||
<div class="avatar-list-item" style="padding: 8px;">
|
<div class="avatar-list-item" style="padding: 8px;">
|
||||||
{var author = $club->getOwner()}
|
{var $author = $club->getOwner()}
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{$author->getURL()}">
|
<a href="{$author->getURL()}">
|
||||||
<img class="ava" src="{$author->getAvatarUrl()}" />
|
<img class="ava" src="{$author->getAvatarUrl()}" />
|
||||||
|
@ -149,7 +151,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$club->getAdministratorsListDisplay() == 1">
|
<div n:if="$club->getAdministratorsListDisplay() == 1">
|
||||||
{var managersCount = $club->getManagersCount(true)}
|
{var $managersCount = $club->getManagersCount(true)}
|
||||||
|
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$managersCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$managersCount});">
|
||||||
{_"administrators"}
|
{_"administrators"}
|
||||||
|
@ -163,7 +165,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="avatar-list">
|
<div class="avatar-list">
|
||||||
<div class="avatar-list-item" n:if="!$club->isOwnerHidden()">
|
<div class="avatar-list-item" n:if="!$club->isOwnerHidden()">
|
||||||
{var author = $club->getOwner()}
|
{var $author = $club->getOwner()}
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{$author->getURL()}">
|
<a href="{$author->getURL()}">
|
||||||
<img class="ava" src="{$author->getAvatarUrl()}" />
|
<img class="ava" src="{$author->getAvatarUrl()}" />
|
||||||
|
@ -175,7 +177,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="avatar-list-item" n:foreach="$club->getManagers(1, true) as $manager">
|
<div class="avatar-list-item" n:foreach="$club->getManagers(1, true) as $manager">
|
||||||
{var user = $manager->getUser()}
|
{var $user = $manager->getUser()}
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{$user->getURL()}">
|
<a href="{$user->getURL()}">
|
||||||
<img height="32" class="ava" src="{$user->getAvatarUrl()}" />
|
<img height="32" class="ava" src="{$user->getAvatarUrl()}" />
|
||||||
|
@ -203,7 +205,7 @@
|
||||||
<div style="padding: 5px;">
|
<div style="padding: 5px;">
|
||||||
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
|
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
|
||||||
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
|
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
|
||||||
{var cover = $album->getCoverPhoto()}
|
{var $cover = $album->getCoverPhoto()}
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURL()}"
|
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURL()}"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<a href="/im">{_my_messages}</a> »
|
<a href="/im">{_my_messages}</a> »
|
||||||
<a href="{$correspondent->getURL()}">{$correspondent->getCanonicalName()}</a>
|
<a href="{$correspondent->getURL()}">{$correspondent->getCanonicalName()}</a>
|
||||||
<div n:if="($online = $correspondent->getOnline()->timestamp()) + 2505600 > time()" style="float: right;">
|
<div n:if="($online = $correspondent->getOnline()->timestamp()) + 2505600 > time()" style="float: right;">
|
||||||
{var diff = date_diff(date_create(), date_create('@' . $online))}
|
{var $diff = date_diff(date_create(), date_create('@' . $online))}
|
||||||
{if 5 >= $diff->i}
|
{if 5 >= $diff->i}
|
||||||
<span><b>{_online}</b></span>
|
<span><b>{_online}</b></span>
|
||||||
{else}
|
{else}
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="messenger-app--input">
|
<div class="messenger-app--input">
|
||||||
{if $correspondent->getId() === $thisUser->getId() || $correspondent->getPrivacyPermission('messages.write', $thisUser)}
|
{if $correspondent->getId() === $thisUser->getId() || $correspondent->getPrivacyPermission('messages.write', $thisUser)}
|
||||||
<img class="ava" src="{$thisUser->getAvatarUrl()}" alt="{$thisUser->getCanonicalName()}" />
|
<img class="ava" src="{$thisUser->getAvatarUrl('miniscule')}" alt="{$thisUser->getCanonicalName()}" />
|
||||||
<div class="messenger-app--input---messagebox">
|
<div class="messenger-app--input---messagebox">
|
||||||
<textarea
|
<textarea
|
||||||
data-bind="value: messageContent, event: { keydown: onTextareaKeyPress }"
|
data-bind="value: messageContent, event: { keydown: onTextareaKeyPress }"
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
placeholder="Введите сообщение"></textarea>
|
placeholder="Введите сообщение"></textarea>
|
||||||
<button class="button" data-bind="click: sendMessage">Отправить</button>
|
<button class="button" data-bind="click: sendMessage">Отправить</button>
|
||||||
</div>
|
</div>
|
||||||
<img class="ava" src="{$correspondent->getAvatarUrl()}" alt="{$correspondent->getCanonicalName()}" />
|
<img class="ava" src="{$correspondent->getAvatarUrl('miniscule')}" alt="{$correspondent->getCanonicalName()}" />
|
||||||
{else}
|
{else}
|
||||||
<div class="blocked" data-localized-text="Вы не можете писать сообщения {$correspondent->getCanonicalName()} из-за его настроек приватности."></div>
|
<div class="blocked" data-localized-text="Вы не можете писать сообщения {$correspondent->getCanonicalName()} из-за его настроек приватности."></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -21,11 +21,11 @@
|
||||||
<div n:foreach="$corresps as $coresp"
|
<div n:foreach="$corresps as $coresp"
|
||||||
class="crp-entry"
|
class="crp-entry"
|
||||||
onmousedown="window.location.href = {$coresp->getURL()};" >
|
onmousedown="window.location.href = {$coresp->getURL()};" >
|
||||||
{var recipient = $coresp->getCorrespondents()[1]}
|
{var $recipient = $coresp->getCorrespondents()[1]}
|
||||||
{var lastMsg = $coresp->getPreviewMessage()}
|
{var $lastMsg = $coresp->getPreviewMessage()}
|
||||||
|
|
||||||
<div class="crp-entry--image">
|
<div class="crp-entry--image">
|
||||||
<img src="{$recipient->getAvatarURL()}"
|
<img src="{$recipient->getAvatarURL('miniscule')}"
|
||||||
alt="Фотография пользователя" />
|
alt="Фотография пользователя" />
|
||||||
</div>
|
</div>
|
||||||
<div class="crp-entry--info">
|
<div class="crp-entry--info">
|
||||||
|
@ -33,10 +33,10 @@
|
||||||
<span>{$lastMsg->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X")}</span>
|
<span>{$lastMsg->getSendTime()->format("%e %B %G" . tr("time_at_sp") . "%X")}</span>
|
||||||
</div>
|
</div>
|
||||||
<div n:class="crp-entry--message, $lastMsg->getUnreadState() ? unread">
|
<div n:class="crp-entry--message, $lastMsg->getUnreadState() ? unread">
|
||||||
{var _author = $lastMsg->getSender()}
|
{var $_author = $lastMsg->getSender()}
|
||||||
|
|
||||||
<div class="crp-entry--message---av" n:if="$_author->getId() === $thisUser->getId()">
|
<div class="crp-entry--message---av" n:if="$_author->getId() === $thisUser->getId()">
|
||||||
<img src="{$_author->getAvatarURL()}"
|
<img src="{$_author->getAvatarURL('miniscule')}"
|
||||||
alt="Фотография пользователя" />
|
alt="Фотография пользователя" />
|
||||||
</div>
|
</div>
|
||||||
<div class="crp-entry--message---text">
|
<div class="crp-entry--message---text">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{block title}{_edit_note}{/block}
|
{block title}{_edit_note}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{var author = $note->getOwner()}
|
{var $author = $note->getOwner()}
|
||||||
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
||||||
»
|
»
|
||||||
<a href="/notes{$author->getId()}">{_notes}</a>
|
<a href="/notes{$author->getId()}">{_notes}</a>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = iterator_to_array($notes)}
|
{var $iterator = iterator_to_array($notes)}
|
||||||
{var page = $paginatorConf->page}
|
{var $page = $paginatorConf->page}
|
||||||
|
|
||||||
{block title}{_notes}{/block}
|
{block title}{_notes}{/block}
|
||||||
|
|
||||||
|
@ -34,15 +34,41 @@
|
||||||
{* BEGIN ELEMENTS DESCRIPTION *}
|
{* BEGIN ELEMENTS DESCRIPTION *}
|
||||||
|
|
||||||
{block specpage}
|
{block specpage}
|
||||||
|
<style>
|
||||||
|
#userContent img {
|
||||||
|
max-width: 245pt;
|
||||||
|
max-height: 200pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#userContent blockquote {
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
border-bottom: 5px solid #969696;
|
||||||
|
padding: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#userContent cite {
|
||||||
|
margin-top: 1em;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#userContent cite::before {
|
||||||
|
content: "— ";
|
||||||
|
}
|
||||||
|
|
||||||
|
#userContent .underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="container_gray" style="background: white; border-top: none;">
|
<div class="container_gray" style="background: white; border-top: none;">
|
||||||
|
|
||||||
{var data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
{var $data = is_array($iterator) ? $iterator : iterator_to_array($iterator)}
|
||||||
{if sizeof($data) > 0}
|
{if sizeof($data) > 0}
|
||||||
|
|
||||||
<div n:foreach="$data as $dat">
|
<div n:foreach="$data as $dat">
|
||||||
<div class="profile_thumb">
|
<div class="profile_thumb">
|
||||||
<a href="{$owner->getURL()}">
|
<a href="{$owner->getURL()}">
|
||||||
<img src="{$owner->getAvatarUrl()}" style="width: 50px;">
|
<img src="{$owner->getAvatarUrl('miniscule')}" style="width: 50px;">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<article class="note_body" id="userContent" style="width: 540px; display: inline-block; margin-bottom: 35px;">
|
<article class="note_body" id="userContent" style="width: 540px; display: inline-block; margin-bottom: 35px;">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{block title}{$note->getName()}{/block}
|
{block title}{$note->getName()}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{var author = $note->getOwner()}
|
{var $author = $note->getOwner()}
|
||||||
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
<a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
||||||
»
|
»
|
||||||
<a href="/notes{$author->getId()}">{_notes}</a>
|
<a href="/notes{$author->getId()}">{_notes}</a>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
{var author = $note->getOwner()}
|
{var $author = $note->getOwner()}
|
||||||
<style>
|
<style>
|
||||||
#userContent img {
|
#userContent img {
|
||||||
max-width: 245pt;
|
max-width: 245pt;
|
||||||
|
@ -33,6 +33,10 @@
|
||||||
#userContent cite::before {
|
#userContent cite::before {
|
||||||
content: "— ";
|
content: "— ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#userContent .underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<article id="userContent" style="margin: 10px 10px 0;">
|
<article id="userContent" style="margin: 10px 10px 0;">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var sorting = false}
|
{var $sorting = false}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
{_feedback}
|
{_feedback}
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getModel(1)->getAvatarUrl()}" width=64 />
|
<img src="{$x->getModel(1)->getAvatarUrl('miniscule')}" width=64 />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{block title}Альбом {$album->getName()}{/block}
|
{block title}Альбом {$album->getName()}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
{var isClub = ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)}
|
{var $isClub = ($album->getOwner() instanceof openvk\Web\Models\Entities\Club)}
|
||||||
|
|
||||||
<a href="{$album->getOwner()->getURL()}">
|
<a href="{$album->getOwner()->getURL()}">
|
||||||
{$album->getOwner()->getCanonicalName()}
|
{$album->getOwner()->getCanonicalName()}
|
||||||
|
|
|
@ -1,16 +1,35 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = iterator_to_array($albums)}
|
{var $iterator = iterator_to_array($albums)}
|
||||||
{var page = $paginatorConf->page}
|
{var $page = $paginatorConf->page}
|
||||||
|
|
||||||
{block title}{_"albums"} {$owner->getCanonicalName()}{/block}
|
{block title}{_"albums"} {$owner->getCanonicalName()}{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
<a href="{$owner->getURL()}">{$owner->getCanonicalName()}</a>
|
{if isset($thisUser) && $thisUser->getId() == $owner->getId()}
|
||||||
» {_"albums"}
|
{_my_photos}
|
||||||
|
{else}
|
||||||
<div n:if="$canEdit" style="float: right;">
|
<a href="{$owner->getURL()}">
|
||||||
{var isClub = ($owner instanceof openvk\Web\Models\Entities\Club)}
|
{$owner->getCanonicalName()}</a>
|
||||||
<a href="/albums/create{$isClub ? '?gpid=' . $owner->getId() : ''}">{_"create_album"}</a>
|
»
|
||||||
|
{_albums}
|
||||||
|
{/if}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block size}
|
||||||
|
<div style="padding-bottom: 0px; padding-top: 0;" class="summaryBar">
|
||||||
|
<div class="summary">
|
||||||
|
{if !is_null($thisUser) && $owner->getId() === $thisUser->getId()}
|
||||||
|
{tr("albums_list", $count)}
|
||||||
|
{else}
|
||||||
|
{tr("albums", $count)}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<span n:if="$canEdit" style="float: right;">
|
||||||
|
|
|
||||||
|
{var $isClub = ($owner instanceof \openvk\Web\Models\Entities\Club)}
|
||||||
|
<a href="/albums/create{$isClub ? '?gpid=' . $owner->getId() : ''}">{_create_album}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -25,8 +44,8 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
{var cover = $x->getCoverPhoto()}
|
{var $cover = $x->getCoverPhoto()}
|
||||||
{var preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURL()}
|
{var $preview = is_null($cover) ? "/assets/packages/static/openvk/img/camera_200.png" : $cover->getURLBySizeId("normal")}
|
||||||
|
|
||||||
<a href="/album{$x->getPrettyId()}">
|
<a href="/album{$x->getPrettyId()}">
|
||||||
<img src="{$preview}" alt="{$x->getName()}" style="height: 130px; width: 170px; object-fit: cover" />
|
<img src="{$preview}" alt="{$x->getName()}" style="height: 130px; width: 170px; object-fit: cover" />
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
<center style="margin-bottom: 8pt;">
|
<center style="margin-bottom: 8pt;">
|
||||||
<img src="{$photo->getURL()}" style="max-width: 80%; max-height: 60vh;" />
|
<img src="{$photo->getURLBySizeId('large')}" style="max-width: 80%; max-height: 60vh;" />
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getAvatarUrl()}" width="75" alt="{_"photo"}" />
|
<img src="{$x->getAvatarUrl('miniscule')}" width="75" alt="{_"photo"}" />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
{if $comment->getUType() === 0}
|
{if $comment->getUType() === 0}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<img src="{$comment->getUser()->getAvatarUrl()}" width="50" />
|
<img src="{$comment->getUser()->getAvatarUrl('miniscule')}" width="50" />
|
||||||
</td>
|
</td>
|
||||||
{else}
|
{else}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
{if $thisUser->getChandlerUser()->can("write")->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
<a href="{$comment->getUser()->getURL()}">
|
<a href="{$comment->getUser()->getURL()}">
|
||||||
<span class="nobold">
|
<span class="nobold">
|
||||||
{var lastName = $comment->getUser()->getLastName()}
|
{var $lastName = $comment->getUser()->getLastName()}
|
||||||
{if empty(trim($lastName))}
|
{if empty(trim($lastName))}
|
||||||
({$comment->getUser()->getFirstName()})
|
({$comment->getUser()->getFirstName()})
|
||||||
{else}
|
{else}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
{var isMain = $mode === 'faq'}
|
{var $isMain = $mode === 'faq'}
|
||||||
{var isNew = $mode === 'new'}
|
{var $isNew = $mode === 'new'}
|
||||||
{var isList = $mode === 'list'}
|
{var $isList = $mode === 'list'}
|
||||||
|
|
||||||
{if $thisUser}
|
{if $thisUser}
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
|
@ -26,16 +26,26 @@
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
{if $isNew}
|
{if $isNew}
|
||||||
<div class="new">
|
{if !is_null($banReason)}
|
||||||
<form action="/support" method="post" style="margin:0;">
|
<center>
|
||||||
<center>
|
<img src="/assets/packages/static/openvk/img/oof.apng" alt="{_'banned_alt'}" style="width: 20%;" />
|
||||||
<input name="name" style="width: 80%; resize: vertical;" placeholder="{_support_new_title}" /><br /><br />
|
</center>
|
||||||
<textarea name="text" style="width: 80%; resize: vertical;" placeholder="{_support_new_content}"></textarea><br /><br />
|
<p>
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
{tr("banned_in_support_1", htmlentities($thisUser->getCanonicalName()))|noescape}<br/>
|
||||||
<input type="submit" value="{_write}" class="button" style="margin-left: 70%;" /><br /><br />
|
{tr("banned_in_support_2", htmlentities($banReason))|noescape}
|
||||||
</center>
|
</p>
|
||||||
</form>
|
{else}
|
||||||
</div>
|
<div class="new">
|
||||||
|
<form action="/support" method="post" style="margin:0;">
|
||||||
|
<center>
|
||||||
|
<input name="name" style="width: 80%; resize: vertical;" placeholder="{_support_new_title}" /><br /><br />
|
||||||
|
<textarea name="text" style="width: 80%; resize: vertical;" placeholder="{_support_new_content}"></textarea><br /><br />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input type="submit" value="{_write}" class="button" style="margin-left: 70%;" /><br /><br />
|
||||||
|
</center>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block description}
|
{block description}
|
||||||
{var author = $x->getUser()}
|
{var $author = $x->getUser()}
|
||||||
|
|
||||||
{ovk_proc_strtr($x->getContext(), 50)}<br/>
|
{ovk_proc_strtr($x->getContext(), 50)}<br/>
|
||||||
<span class="nobold">{_author}: </span> <a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
<span class="nobold">{_author}: </span> <a href="{$author->getURL()}">{$author->getCanonicalName()}</a>
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
{if $comment->getUType() === 0}
|
{if $comment->getUType() === 0}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
<img src="{$comment->getUser()->getAvatarUrl()}" width="50" />
|
<img src="{$comment->getUser()->getAvatarUrl('miniscule')}" width="50" />
|
||||||
</td>
|
</td>
|
||||||
{else}
|
{else}
|
||||||
<td width="54" valign="top">
|
<td width="54" valign="top">
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
|
|
||||||
{if $comment->getUType() === 1}
|
{if $comment->getUType() === 1}
|
||||||
<div class="post-menu">
|
<div class="post-menu">
|
||||||
{var isLikedByUser = $comment->isLikedByUser()}
|
{var $isLikedByUser = $comment->isLikedByUser()}
|
||||||
<strong id="markText-{$comment->getId()}">
|
<strong id="markText-{$comment->getId()}">
|
||||||
{if !is_null($isLikedByUser)}
|
{if !is_null($isLikedByUser)}
|
||||||
{if $comment->isLikedByUser()}
|
{if $comment->isLikedByUser()}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = iterator_to_array($topics)}
|
{var $iterator = iterator_to_array($topics)}
|
||||||
{var page = $paginatorConf->page}
|
{var $page = $paginatorConf->page}
|
||||||
|
|
||||||
{block title}{_discussions} {$club->getCanonicalName()}{/block}
|
{block title}{_discussions} {$club->getCanonicalName()}{/block}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
<div style="float: left;">
|
<div style="float: left;">
|
||||||
{tr("messages", $x->getCommentsCount())}
|
{tr("messages", $x->getCommentsCount())}
|
||||||
</div>
|
</div>
|
||||||
{var lastComment = $x->getLastComment()}
|
{var $lastComment = $x->getLastComment()}
|
||||||
<div n:if="$lastComment" class="avatar-list-item" style="float: right;">
|
<div n:if="$lastComment" class="avatar-list-item" style="float: right;">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{$lastComment->getOwner()->getURL()}">
|
<a href="{$lastComment->getOwner()->getURL()}">
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
|
||||||
{var isMain = $mode === 'main'}
|
{var $isMain = $mode === 'main'}
|
||||||
{var isContacts = $mode === 'contacts'}
|
{var $isContacts = $mode === 'contacts'}
|
||||||
{var isInterests = $mode === 'interests'}
|
{var $isInterests = $mode === 'interests'}
|
||||||
{var isAvatar = $mode === 'avatar'}
|
{var $isAvatar = $mode === 'avatar'}
|
||||||
<div n:if="$user->hasPendingNumberChange()" class="msg">
|
<div n:if="$user->hasPendingNumberChange()" class="msg">
|
||||||
<b>Подтверждение номера телефона</b><br/>
|
<b>Подтверждение номера телефона</b><br/>
|
||||||
Введите код для подтверждения смены номера: <a href="/edit/verify_phone">ввести код</a>.
|
Введите код для подтверждения смены номера: <a href="/edit/verify_phone">ввести код</a>.
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var perPage = 6} {* Why 6? Check User::_abstractRelationGenerator *}
|
{var $perPage = 6} {* Why 6? Check User::_abstractRelationGenerator *}
|
||||||
|
|
||||||
{var act = $_GET["act"] ?? "friends"}
|
{var $act = $_GET["act"] ?? "friends"}
|
||||||
|
|
||||||
{if $act == "incoming"}
|
{if $act == "incoming"}
|
||||||
{var iterator = iterator_to_array($user->getFollowers($page))}
|
{var $iterator = iterator_to_array($user->getFollowers($page))}
|
||||||
{var count = $user->getFollowersCount()}
|
{var $count = $user->getFollowersCount()}
|
||||||
{elseif $act == "outcoming"}
|
{elseif $act == "outcoming"}
|
||||||
{var iterator = iterator_to_array($user->getSubscriptions($page))}
|
{var $iterator = iterator_to_array($user->getSubscriptions($page))}
|
||||||
{var count = $user->getSubscriptionsCount()}
|
{var $count = $user->getSubscriptionsCount()}
|
||||||
{else}
|
{else}
|
||||||
{var iterator = iterator_to_array($user->getFriends($page))}
|
{var $iterator = iterator_to_array($user->getFriends($page))}
|
||||||
{var count = $user->getFriendsCount()}
|
{var $count = $user->getFriendsCount()}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
|
@ -25,13 +25,17 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block header}
|
{block header}
|
||||||
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
{if isset($thisUser) && $thisUser->getId() == $user->getId()}
|
||||||
{if $act == "incoming"}
|
{_my_friends}
|
||||||
{_"incoming_req"}
|
|
||||||
{elseif $act == "outcoming"}
|
|
||||||
{_"outcoming_req"}
|
|
||||||
{else}
|
{else}
|
||||||
{_"friends"}
|
<a href="{$user->getURL()}">{$user->getCanonicalName()}</a> »
|
||||||
|
{if $act == "incoming"}
|
||||||
|
{_"incoming_req"}
|
||||||
|
{elseif $act == "outcoming"}
|
||||||
|
{_"outcoming_req"}
|
||||||
|
{else}
|
||||||
|
{_"friends"}
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -39,11 +43,38 @@
|
||||||
<div n:attr="id => ($act === 'friends' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'friends' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'friends' ? 'act_tab_a' : 'ki')" href="?">{_friends}</a>
|
<a n:attr="id => ($act === 'friends' ? 'act_tab_a' : 'ki')" href="?">{_friends}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($act === 'incoming' ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'activetabs' : 'ki')" class="tab">
|
||||||
<a n:attr="id => ($act === 'incoming' ? 'act_tab_a' : 'ki')" href="?act=incoming">{_incoming_req}</a>
|
<a n:attr="id => ($act === 'incoming' || $act === 'outcoming' ? 'act_tab_a' : 'ki')" href="?act=incoming">{_req}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:attr="id => ($act === 'outcoming' ? 'activetabs' : 'ki')" class="tab">
|
{/block}
|
||||||
<a n:attr="id => ($act === 'outcoming' ? 'act_tab_a' : 'ki')" href="?act=outcoming">{_outcoming_req}</a>
|
|
||||||
|
{block size}
|
||||||
|
<div n:if="$act === 'incoming' || $act === 'outcoming'" class="mb_tabs">
|
||||||
|
<div n:attr="id => ($act === 'incoming' ? 'active' : 'ki')" class="mb_tab">
|
||||||
|
<div>
|
||||||
|
<a href="?act=incoming">{_incoming_req}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div n:attr="id => ($act === 'outcoming' ? 'active' : 'ki')" class="mb_tab">
|
||||||
|
<div>
|
||||||
|
<a href="?act=outcoming">{_outcoming_req}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="padding-bottom: 0px;" class="summaryBar">
|
||||||
|
<div class="summary">
|
||||||
|
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
|
||||||
|
{if $act == "incoming"}
|
||||||
|
{tr("req", $count)}
|
||||||
|
{elseif $act == "outcoming"}
|
||||||
|
{tr("req", $count)}
|
||||||
|
{else}
|
||||||
|
{tr("friends_list", $count)}
|
||||||
|
{/if}
|
||||||
|
{else}
|
||||||
|
{tr("friends", $count)}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
@ -54,7 +85,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getAvatarUrl()}" width="75" alt="Фотография группы" />
|
<img src="{$x->getAvatarUrl('miniscule')}" width="75" alt="Фотография пользователя" />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
|
@ -82,7 +113,7 @@
|
||||||
|
|
||||||
{block actions}
|
{block actions}
|
||||||
{if $x->getId() !== $thisUser->getId()}
|
{if $x->getId() !== $thisUser->getId()}
|
||||||
{var subStatus = $x->getSubscriptionStatus($thisUser)}
|
{var $subStatus = $x->getSubscriptionStatus($thisUser)}
|
||||||
{if $subStatus === 0}
|
{if $subStatus === 0}
|
||||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||||
<input type="hidden" name="act" value="add" />
|
<input type="hidden" name="act" value="add" />
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = $user->getClubs($page, $admin)}
|
{var $iterator = $user->getClubs($page, $admin)}
|
||||||
{var count = $user->getClubCount($admin)}
|
{var $count = $user->getClubCount($admin)}
|
||||||
|
|
||||||
{block title}
|
{block title}
|
||||||
{_groups}
|
{_groups}
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block size}
|
{block size}
|
||||||
<div n:if="!is_null($thisUser) && $user->getId() === $thisUser->getId()" style="padding-bottom: 0px; border-bottom: 0;" class="summaryBar">
|
<div style="padding-bottom: 0px;" class="summaryBar">
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
|
{if !is_null($thisUser) && $user->getId() === $thisUser->getId()}
|
||||||
{tr("groups_list", $thisUser->getClubCount())}
|
{tr("groups_list", $thisUser->getClubCount())}
|
||||||
|
@ -48,12 +48,12 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getAvatarUrl()}" width="75" alt="Фотография группы" />
|
<img src="{$x->getAvatarUrl('miniscule')}" width="75" alt="Фотография группы" />
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}{/block}
|
{block name}{/block}
|
||||||
|
|
||||||
{block infoTable}
|
{block infotable}
|
||||||
<table id="basicInfo" class="ugc-table group_info" cellspacing="0" cellpadding="0" border="0">
|
<table id="basicInfo" class="ugc-table group_info" cellspacing="0" cellpadding="0" border="0">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -73,26 +73,28 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block actions}
|
{block actions}
|
||||||
{var clubPinned = $thisUser->isClubPinned($x)}
|
{var $clubPinned = $thisUser->isClubPinned($x)}
|
||||||
{if $x->canBeModifiedBy($thisUser ?? NULL)}
|
{if $x->canBeModifiedBy($thisUser ?? NULL)}
|
||||||
<a style="width: 140px;" class="profile_link" href="{$x->getURL()}">
|
<div class="navigation" style="width: 140px;">
|
||||||
{_check_community}
|
<a class="link" href="{$x->getURL()}">
|
||||||
</a>
|
{_check_community}
|
||||||
{if ($clubPinned || $thisUser->getPinnedClubCount() <= 10)}
|
|
||||||
<a style="width: 140px;" class="profile_link" href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
|
||||||
{if $clubPinned}
|
|
||||||
{_remove_from_left_menu}
|
|
||||||
{else}
|
|
||||||
{_add_to_left_menu}
|
|
||||||
{/if}
|
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{if ($clubPinned || $thisUser->getPinnedClubCount() <= 10)}
|
||||||
<form action="/setSub/club" method="post">
|
<a class="link" href="/groups_pin?club={$x->getId()}&hash={rawurlencode($csrfToken)}" id="_pinGroup" data-group-name="{$x->getName()}" data-group-url="{$x->getUrl()}">
|
||||||
<input type="hidden" name="act" value="rem" />
|
{if $clubPinned}
|
||||||
<input type="hidden" name="id" value="{$x->getId()}" />
|
{_remove_from_left_menu}
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
{else}
|
||||||
<input style="width: 140px; text-transform: lowercase;" type="submit" id="profile_link" value="{_leave_community}" />
|
{_add_to_left_menu}
|
||||||
</form>
|
{/if}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
<form action="/setSub/club" method="post">
|
||||||
|
<input type="hidden" name="act" value="rem" />
|
||||||
|
<input type="hidden" name="id" value="{$x->getId()}" />
|
||||||
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
|
<input style="text-transform: lowercase; width: 100%;" class="link" type="submit" value="{_"leave_community"}" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
|
|
||||||
{block content}
|
{block content}
|
||||||
|
|
||||||
{var isMain = $mode === 'main'}
|
{var $isMain = $mode === 'main'}
|
||||||
{var isPrivacy = $mode === 'privacy'}
|
{var $isPrivacy = $mode === 'privacy'}
|
||||||
{var isFinance = $mode === 'finance'}
|
{var $isFinance = $mode === 'finance'}
|
||||||
{var isFinanceTU = $mode === 'finance.top-up'}
|
{var $isFinanceTU = $mode === 'finance.top-up'}
|
||||||
{var isInterface = $mode === 'interface'}
|
{var $isInterface = $mode === 'interface'}
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
<div n:attr="id => ($isMain ? 'activetabs' : 'ki')" class="tab">
|
||||||
|
@ -347,16 +347,22 @@
|
||||||
<center>{tr("also_you_can_transfer_points", $thisUser->getCoins(), rawurlencode($csrfToken))|noescape}</center>
|
<center>{tr("also_you_can_transfer_points", $thisUser->getCoins(), rawurlencode($csrfToken))|noescape}</center>
|
||||||
</div>
|
</div>
|
||||||
<div style="width: 22%; float: right;">
|
<div style="width: 22%; float: right;">
|
||||||
<p style="margin: 0; font-size: medium; text-align: center;">
|
<div style="margin: 0; font-size: medium; text-align: center; font-weight: 900;">
|
||||||
<b>
|
{_on_your_account}
|
||||||
{_on_your_account}<br/>
|
<div style="width: 100%; height: 60px; font-weight: 100;" id="balance">{$thisUser->getCoins()}</div>
|
||||||
<span style="font-size: 50px;">{$thisUser->getCoins()}</span><br/>
|
{_points_count}<br/>
|
||||||
{_points_count}<br/><br/>
|
<small><a href="?act=finance.top-up">[{_have_voucher}?]</a></small>
|
||||||
<small><a href="?act=finance.top-up">[{_have_voucher}?]</a></small>
|
|
||||||
</b>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{script "js/node_modules/textfit/textFit.min.js"}
|
||||||
|
<script>
|
||||||
|
let balance = document.querySelector("#balance");
|
||||||
|
|
||||||
|
balance.style.width = Math.ceil(balance.parentNode.getBoundingClientRect().width) + "px";
|
||||||
|
textFit(balance, { alignVert: true });
|
||||||
|
</script>
|
||||||
|
|
||||||
{elseif $isFinanceTU}
|
{elseif $isFinanceTU}
|
||||||
|
|
||||||
<p>{_voucher_explanation} {_voucher_explanation_ex}</p>
|
<p>{_voucher_explanation} {_voucher_explanation_ex}</p>
|
||||||
|
@ -407,7 +413,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select name="style_avatar">
|
<select name="style_avatar">
|
||||||
<option value="0" {if $user->getStyleAvatar() == 0}selected{/if}>{_"default"}</option>
|
<option value="0" {if $user->getStyleAvatar() == 0}selected{/if}>{_"arbitrary_avatars"} ({_"default"})</option>
|
||||||
<option value="1" {if $user->getStyleAvatar() == 1}selected{/if}>{_"cut"}</option>
|
<option value="1" {if $user->getStyleAvatar() == 1}selected{/if}>{_"cut"}</option>
|
||||||
<option value="2" {if $user->getStyleAvatar() == 2}selected{/if}>{_"round_avatars"}</option>
|
<option value="2" {if $user->getStyleAvatar() == 2}selected{/if}>{_"round_avatars"}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<!-- openGraph -->
|
<!-- openGraph -->
|
||||||
<meta property="og:title" content="{$user->getCanonicalName()}" />
|
<meta property="og:title" content="{$user->getCanonicalName()}" />
|
||||||
<meta property="og:url" content="http://{$_SERVER['HTTP_HOST']}{$user->getURL()}" />
|
<meta property="og:url" content="http://{$_SERVER['HTTP_HOST']}{$user->getURL()}" />
|
||||||
<meta property="og:image" content="{$user->getAvatarUrl()}" />
|
<meta property="og:image" content="{$user->getAvatarUrl('normal')}" />
|
||||||
<meta property="og:type" content="profile" />
|
<meta property="og:type" content="profile" />
|
||||||
<meta property="og:first_name" content="{$user->getFirstName()}" />
|
<meta property="og:first_name" content="{$user->getFirstName()}" />
|
||||||
<meta property="og:last_name" content="{$user->getLastName()}" />
|
<meta property="og:last_name" content="{$user->getLastName()}" />
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
<div class="left_small_block">
|
<div class="left_small_block">
|
||||||
<div>
|
<div>
|
||||||
<a href="{$user->getAvatarLink()|nocheck}">
|
<a href="{$user->getAvatarLink()|nocheck}">
|
||||||
<img src="{$user->getAvatarUrl()}"
|
<img src="{$user->getAvatarUrl('normal')}"
|
||||||
alt="{$user->getCanonicalName()}"
|
alt="{$user->getCanonicalName()}"
|
||||||
style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
style="width: 100%; image-rendering: -webkit-optimize-contrast;" />
|
||||||
</a>
|
</a>
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
<div id="profile_link" style="width: 194px;">
|
<div id="profile_link" style="width: 194px;">
|
||||||
<a href="/edit" class="link">{_"edit_page"}</a>
|
<a href="/edit" class="link">{_"edit_page"}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce']" id="profile_link" style="width: 194px;">
|
<div n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && !$thisUser->prefersNotToSeeRating()" id="profile_link" style="width: 194px;">
|
||||||
<a onClick="showIncreaseRatingDialog({$thisUser->getCoins()}, {ltrim($thisUser->getUrl(), '/')}, {$csrfToken})" class="link">{_increase_rating}</a>
|
<a onClick="showIncreaseRatingDialog({$thisUser->getCoins()}, {ltrim($thisUser->getUrl(), '/')}, {$csrfToken})" class="link">{_increase_rating}</a>
|
||||||
</div>
|
</div>
|
||||||
{else}
|
{else}
|
||||||
|
@ -93,11 +93,21 @@
|
||||||
{_warn_user_action}
|
{_warn_user_action}
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{if $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)}
|
||||||
|
<a href="javascript:toggleBanInSupport()" class="profile_link">
|
||||||
|
{if $user->isBannedInSupport()}
|
||||||
|
{_unban_in_support_user_action}
|
||||||
|
{else}
|
||||||
|
{_ban_in_support_user_action}
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
|
<a n:if="OPENVK_ROOT_CONF['openvk']['preferences']['commerce'] && $user->getGiftCount() == 0" href="/gifts?act=pick&user={$user->getId()}" class="profile_link">{_send_gift}</a>
|
||||||
<a n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a>
|
<a n:if="$user->getPrivacyPermission('messages.write', $thisUser)" href="/im?sel={$user->getId()}" class="profile_link">{_"send_message"}</a>
|
||||||
|
|
||||||
{var subStatus = $user->getSubscriptionStatus($thisUser)}
|
{var $subStatus = $user->getSubscriptionStatus($thisUser)}
|
||||||
{if $subStatus === 0}
|
{if $subStatus === 0}
|
||||||
<form action="/setSub/user" method="post" class="profile_link_form">
|
<form action="/setSub/user" method="post" class="profile_link_form">
|
||||||
<input type="hidden" name="act" value="add" />
|
<input type="hidden" name="act" value="add" />
|
||||||
|
@ -131,7 +141,7 @@
|
||||||
<a n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
|
<a n:if="$user->getFollowersCount() > 0" href="/friends{$user->getId()}?act=incoming" class="profile_link">{tr("followers", $user->getFollowersCount())}</a>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints">
|
<div n:if="isset($thisUser) && !$thisUser->prefersNotToSeeRating()" class="profile-hints">
|
||||||
{var completeness = $user->getProfileCompletenessReport()}
|
{var $completeness = $user->getProfileCompletenessReport()}
|
||||||
|
|
||||||
<div n:class="completeness-gauge, $completeness->total >= 100 ? completeness-gauge-gold">
|
<div n:class="completeness-gauge, $completeness->total >= 100 ? completeness-gauge-gold">
|
||||||
<div style="width: {$completeness->percent}%"></div>
|
<div style="width: {$completeness->percent}%"></div>
|
||||||
|
@ -164,7 +174,7 @@
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)">
|
<div n:if="$user->getFriendsCount() > 0 && $user->getPrivacyPermission('friends.read', $thisUser ?? NULL)">
|
||||||
{var friendCount = $user->getFriendsCount()}
|
{var $friendCount = $user->getFriendsCount()}
|
||||||
|
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$friendCount});">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$friendCount});">
|
||||||
{_"friends"}
|
{_"friends"}
|
||||||
|
@ -180,7 +190,7 @@
|
||||||
<div class="cl_element" n:foreach="$user->getFriends(1) as $friend">
|
<div class="cl_element" n:foreach="$user->getFriends(1) as $friend">
|
||||||
<div class="cl_avatar">
|
<div class="cl_avatar">
|
||||||
<a href="{$friend->getURL()}">
|
<a href="{$friend->getURL()}">
|
||||||
<img class="ava" src="{$friend->getAvatarUrl()}" />
|
<img class="ava" src="{$friend->getAvatarUrl('miniscule')}" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="{$friend->getURL()}" class="cl_name">
|
<a href="{$friend->getURL()}" class="cl_name">
|
||||||
|
@ -205,10 +215,10 @@
|
||||||
<div style="padding: 5px;">
|
<div style="padding: 5px;">
|
||||||
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
|
<div class="ovk-album" style="display: inline-block;" n:foreach="$albums as $album">
|
||||||
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
|
<div style="text-align: center;float: left;height: 54pt;width: 100px;">
|
||||||
{var cover = $album->getCoverPhoto()}
|
{var $cover = $album->getCoverPhoto()}
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURL()}"
|
src="{is_null($cover)?'/assets/packages/static/openvk/img/camera_200.png':$cover->getURLBySizeId('small')}"
|
||||||
style="max-width: 80px; max-height: 54pt;" />
|
style="max-width: 80px; max-height: 54pt;" />
|
||||||
</div>
|
</div>
|
||||||
<div style="overflow: hidden; overflow-wrap: break-word;">
|
<div style="overflow: hidden; overflow-wrap: break-word;">
|
||||||
|
@ -274,7 +284,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$user->getClubCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
|
<div n:if="$user->getClubCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
|
||||||
{var clubsCount = $user->getClubCount()}
|
{var $clubsCount = $user->getClubCount()}
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$clubsCount})">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$clubsCount})">
|
||||||
{_"groups"}
|
{_"groups"}
|
||||||
</div>
|
</div>
|
||||||
|
@ -293,7 +303,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$user->getMeetingCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
|
<div n:if="$user->getMeetingCount() > 0 && $user->getPrivacyPermission('groups.read', $thisUser ?? NULL)">
|
||||||
{var meetingCount = $user->getMeetingCount()}
|
{var $meetingCount = $user->getMeetingCount()}
|
||||||
<div class="content_title_expanded" onclick="hidePanel(this, {$meetingCount})">
|
<div class="content_title_expanded" onclick="hidePanel(this, {$meetingCount})">
|
||||||
{_meetings}
|
{_meetings}
|
||||||
</div>
|
</div>
|
||||||
|
@ -317,7 +327,7 @@
|
||||||
<div class="right_big_block">
|
<div class="right_big_block">
|
||||||
<div class="page_info">
|
<div class="page_info">
|
||||||
<div n:if="!is_null($alert = $user->getAlert())" class="user-alert">{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}</div>
|
<div n:if="!is_null($alert = $user->getAlert())" class="user-alert">{strpos($alert, "@") === 0 ? tr(substr($alert, 1)) : $alert}</div>
|
||||||
{var thatIsThisUser = isset($thisUser) && $user->getId() == $thisUser->getId()}
|
{var $thatIsThisUser = isset($thisUser) && $user->getId() == $thisUser->getId()}
|
||||||
<div n:if="$thatIsThisUser" class="page_status_popup" id="status_editor" style="display: none;">
|
<div n:if="$thatIsThisUser" class="page_status_popup" id="status_editor" style="display: none;">
|
||||||
<form name="status_popup_form" onsubmit="changeStatus(); return false;">
|
<form name="status_popup_form" onsubmit="changeStatus(); return false;">
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px;">
|
||||||
|
@ -485,7 +495,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="content_list long">
|
<div class="content_list long">
|
||||||
<div class="cl_element" style="width: 25%;" n:foreach="$user->getGifts(1, 4) as $giftDescriptor">
|
<div class="cl_element" style="width: 25%;" n:foreach="$user->getGifts(1, 4) as $giftDescriptor">
|
||||||
{var hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
|
{var $hideInfo = !is_null($thisUser) ? ($giftDescriptor->anon ? $thisUser->getId() !== $user->getId() : false) : false}
|
||||||
<div class="cl_avatar">
|
<div class="cl_avatar">
|
||||||
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
|
<a href="{$hideInfo ? 'javascript:false' : $giftDescriptor->sender->getURL()}">
|
||||||
<img style="width: 70px; max-height: 70px;"
|
<img style="width: 70px; max-height: 70px;"
|
||||||
|
@ -512,7 +522,7 @@
|
||||||
xhr = new XMLHttpRequest();
|
xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
|
xhr.open("GET", "/admin/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
xhr.onload = (function() {
|
xhr.onload = (function() {
|
||||||
if(xhr.responseText.indexOf("reason") === -1)
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]);
|
MessageBox("Ошибка", "Не удалось забанить пользователя...", ["OK"], [Function.noop]);
|
||||||
else
|
else
|
||||||
MessageBox("Операция успешна", "Пользователь заблокирован", ["OK"], [Function.noop]);
|
MessageBox("Операция успешна", "Пользователь заблокирован", ["OK"], [Function.noop]);
|
||||||
|
@ -526,7 +536,7 @@
|
||||||
function warnUser() {
|
function warnUser() {
|
||||||
uBanMsgTxt = "Вы собираетесь предупредить пользователя " + {$user->getCanonicalName()} + ".";
|
uBanMsgTxt = "Вы собираетесь предупредить пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
uBanMsgTxt += "<br/>Мы отправим уведомление пользователю в личные сообщения от имени аккаунта администратора.";
|
uBanMsgTxt += "<br/>Мы отправим уведомление пользователю в личные сообщения от имени аккаунта администратора.";
|
||||||
uBanMsgTxt += "<br/><br/><b>Текст предупреждения</b>: <input type='text' id='uWarnMsgInput' placeholder='придумайте что-нибудь крутое' />"
|
uBanMsgTxt += "<br/><br/><b>Текст предупреждения</b>: <input type='text' id='uWarnMsgInput' placeholder='придумайте что-нибудь крутое' />";
|
||||||
|
|
||||||
MessageBox("Выдать предупреждение " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
MessageBox("Выдать предупреждение " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
(function() {
|
(function() {
|
||||||
|
@ -546,6 +556,51 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script n:if="isset($thisUser) && $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
||||||
|
{if $user->isBannedInSupport()}
|
||||||
|
function toggleBanInSupport() {
|
||||||
|
uBanMsgTxt = "Вы собираетесь разблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
|
uBanMsgTxt += "<br/>Сейчас он заблокирован по причине <strong>" + {$user->getBanInSupportReason()} + "</strong>.";
|
||||||
|
|
||||||
|
MessageBox("Разблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
|
(function() {
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/admin/support/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
|
MessageBox("Ошибка", "Не удалось разблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox("Операция успешна", "Пользователь разблокирован в поддержке", ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null);
|
||||||
|
}),
|
||||||
|
Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
{else}
|
||||||
|
function toggleBanInSupport() {
|
||||||
|
uBanMsgTxt = "Вы собираетесь заблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
|
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />";
|
||||||
|
|
||||||
|
MessageBox("Заблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
|
(function() {
|
||||||
|
res = document.querySelector("#uBanMsgInput").value;
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/admin/support/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
|
MessageBox("Ошибка", "Не удалось заблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox("Операция успешна", "Пользователь заблокирован в поддержке", ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null);
|
||||||
|
}),
|
||||||
|
Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
{/if}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script n:if="isset($thisUser) && $user->getId() == $thisUser->getId()" n:syntax="off">
|
<script n:if="isset($thisUser) && $user->getId() == $thisUser->getId()" n:syntax="off">
|
||||||
function setStatusEditorShown(shown) {
|
function setStatusEditorShown(shown) {
|
||||||
document.getElementById("status_editor").style.display = shown ? "block" : "none";
|
document.getElementById("status_editor").style.display = shown ? "block" : "none";
|
||||||
|
|
|
@ -4,4 +4,86 @@
|
||||||
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
{tr("user_banned", htmlentities($user->getFirstName()))|noescape}<br/>
|
||||||
{_"user_banned_comment"} <b>{$user->getBanReason()}</b>.
|
{_"user_banned_comment"} <b>{$user->getBanReason()}</b>.
|
||||||
</p>
|
</p>
|
||||||
|
{if isset($thisUser)}
|
||||||
|
<p n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL) || $thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
||||||
|
<br />
|
||||||
|
<a n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)" href="javascript:unbanUser()" class="button">{_unban_user_action}</a>
|
||||||
|
<a n:if="$thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)" href="javascript:toggleBanInSupport()" class="button">
|
||||||
|
{if $user->isBannedInSupport()}
|
||||||
|
{_unban_in_support_user_action}
|
||||||
|
{else}
|
||||||
|
{_ban_in_support_user_action}
|
||||||
|
{/if}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
|
{if isset($thisUser)}
|
||||||
|
<script n:if="$thisUser->getChandlerUser()->can('access')->model('admin')->whichBelongsTo(NULL)">
|
||||||
|
function unbanUser() {
|
||||||
|
uUnbanMsgTxt = "Вы собираетесь разбанить пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
|
uUnbanMsgTxt += "<br/>Сейчас он заблокирован по причине: <strong>" + {$user->getBanReason()} + "</strong>.";
|
||||||
|
|
||||||
|
MessageBox("Разбанить " + {$user->getFirstName()}, uUnbanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
|
(function() {
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/admin/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
|
MessageBox("Ошибка", "Не удалось разблокировать пользователя...", ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox("Операция успешна", "Пользователь разблокирован", ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null);
|
||||||
|
}),
|
||||||
|
Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script n:if="$thisUser->getChandlerUser()->can('write')->model('openvk\Web\Models\Entities\TicketReply')->whichBelongsTo(0)">
|
||||||
|
{if $user->isBannedInSupport()}
|
||||||
|
function toggleBanInSupport() {
|
||||||
|
uBanMsgTxt = "Вы собираетесь разблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
|
uBanMsgTxt += "<br/>Сейчас он заблокирован по причине <strong>" + {$user->getBanInSupportReason()} + "</strong>.";
|
||||||
|
|
||||||
|
MessageBox("Разблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
|
(function() {
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/admin/support/unban/" + {$user->getId()} + "?hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
|
MessageBox("Ошибка", "Не удалось разблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox("Операция успешна", "Пользователь разблокирован в поддержке", ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null);
|
||||||
|
}),
|
||||||
|
Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
{else}
|
||||||
|
function toggleBanInSupport() {
|
||||||
|
uBanMsgTxt = "Вы собираетесь заблокировать в поддержке пользователя " + {$user->getCanonicalName()} + ".";
|
||||||
|
uBanMsgTxt += "<br/><br/><b>Причина бана</b>: <input type='text' id='uBanMsgInput' placeholder='придумайте что-нибудь крутое' />";
|
||||||
|
|
||||||
|
MessageBox("Заблокировать в поддержке " + {$user->getFirstName()}, uBanMsgTxt, ["Подтвердить", "Отмена"], [
|
||||||
|
(function() {
|
||||||
|
res = document.querySelector("#uBanMsgInput").value;
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("GET", "/admin/support/ban/" + {$user->getId()} + "?reason=" + res + "&hash=" + {rawurlencode($csrfToken)}, true);
|
||||||
|
xhr.onload = (function() {
|
||||||
|
if(xhr.responseText.indexOf("success") === -1)
|
||||||
|
MessageBox("Ошибка", "Не удалось заблокировать пользователя в поддержке...", ["OK"], [Function.noop]);
|
||||||
|
else
|
||||||
|
MessageBox("Операция успешна", "Пользователь заблокирован в поддержке", ["OK"], [Function.noop]);
|
||||||
|
});
|
||||||
|
xhr.send(null);
|
||||||
|
}),
|
||||||
|
Function.noop
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
{/if}
|
||||||
|
</script>
|
||||||
|
{/if}
|
||||||
|
|
14
Web/Presenters/templates/User/deleted.xml
Normal file
14
Web/Presenters/templates/User/deleted.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{extends "../@layout.xml"}
|
||||||
|
{block title}{_information}{/block}
|
||||||
|
|
||||||
|
{block header}
|
||||||
|
{_information}
|
||||||
|
{/block}
|
||||||
|
|
||||||
|
{block content}
|
||||||
|
<center style="background: white;border: #DEDEDE solid 1px;">
|
||||||
|
<span style="color: #707070;margin: 60px 0;display: block;">
|
||||||
|
{_profile_not_found_text}
|
||||||
|
</span>
|
||||||
|
</center>
|
||||||
|
{/block}
|
|
@ -1,7 +1,7 @@
|
||||||
{extends "../@listView.xml"}
|
{extends "../@listView.xml"}
|
||||||
{var iterator = $videos}
|
{var $iterator = $videos}
|
||||||
{var count = $paginatorConf->count}
|
{var $count = $paginatorConf->count}
|
||||||
{var page = $paginatorConf->page}
|
{var $page = $paginatorConf->page}
|
||||||
|
|
||||||
{block title}{_"videos"} {$user->getCanonicalName()}{/block}
|
{block title}{_"videos"} {$user->getCanonicalName()}{/block}
|
||||||
|
|
||||||
|
@ -11,13 +11,13 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block size}
|
{block size}
|
||||||
<div style="padding-bottom: 0px;border-bottom: 0; padding-top: 0;" class="summaryBar">
|
<div style="padding-bottom: 0px; padding-top: 0;" class="summaryBar">
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
{tr("videos", $count)}
|
{tr("videos", $count)}
|
||||||
<span n:if="isset($thisUser) && $thisUser->getId() == $user->getId()">
|
<span n:if="isset($thisUser) && $thisUser->getId() == $user->getId()">
|
||||||
|
|
|
|
||||||
<a href="/videos/upload">{_upload_video}</a>
|
<a href="/videos/upload">{_upload_video}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
@ -33,9 +33,11 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block preview}
|
{block preview}
|
||||||
<img src="{$x->getThumbnailURL()}"
|
<div class="video-preview">
|
||||||
alt="{$x->getName()}"
|
<img src="{$x->getThumbnailURL()}"
|
||||||
style="max-height: 43pt; max-width: 140px; height: unset; width: unset; border-radius: unset;" />
|
alt="{$x->getName()}"
|
||||||
|
style="max-width: 170px; max-height: 127px; margin: auto;" />
|
||||||
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block name}
|
{block name}
|
||||||
|
@ -43,7 +45,13 @@
|
||||||
{/block}
|
{/block}
|
||||||
|
|
||||||
{block description}
|
{block description}
|
||||||
<span>{$x->getDescription() ?? ""}</span><br/>
|
<p>
|
||||||
|
<span>{$x->getDescription() ?? ""}</span>
|
||||||
|
</p>
|
||||||
<span style="color: grey;">{_"video_uploaded"} {$x->getPublicationTime()}</span><br/>
|
<span style="color: grey;">{_"video_uploaded"} {$x->getPublicationTime()}</span><br/>
|
||||||
<span style="color: grey;">{_"video_updated"} {$x->getEditTime() ?? $x->getPublicationTime()}</span>
|
<span style="color: grey;">{_"video_updated"} {$x->getEditTime() ?? $x->getPublicationTime()}</span>
|
||||||
|
<p>
|
||||||
|
<a href="/video{$x->getPrettyId()}">{_view_video}</a>
|
||||||
|
{if $x->getCommentsCount() > 0}| <a href="/video{$x->getPrettyId()}#comments">{_"comments"} ({$x->getCommentsCount()})</a>{/if}
|
||||||
|
</p>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
{if $video->getType() === 0}
|
{if $video->getType() === 0}
|
||||||
<video width="610" src="{$video->getURL()}" controls></video>
|
<video width="610" src="{$video->getURL()}" controls></video>
|
||||||
{else}
|
{else}
|
||||||
{var driver = $video->getVideoDriver()}
|
{var $driver = $video->getVideoDriver()}
|
||||||
{if !$driver}
|
{if !$driver}
|
||||||
Эта видеозапись не поддерживается в вашей версии OpenVK.
|
Эта видеозапись не поддерживается в вашей версии OpenVK.
|
||||||
{else}
|
{else}
|
||||||
|
|
|
@ -27,10 +27,9 @@
|
||||||
<div style="float: left; min-height: 100px; width: 32%;">
|
<div style="float: left; min-height: 100px; width: 32%;">
|
||||||
<h4>{_actions}</h4>
|
<h4>{_actions}</h4>
|
||||||
{if isset($thisUser)}
|
{if isset($thisUser)}
|
||||||
{var canDelete = $post->canBeDeletedBy($thisUser)}
|
{var $canDelete = $post->canBeDeletedBy($thisUser)}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<a n:if="$canDelete ?? false" class="profile_link" style="display:block;width:96%;" href="/wall{$post->getPrettyId()}/delete">{_delete}</a>
|
<a n:if="$canDelete ?? false" class="profile_link" style="display:block;width:96%;" href="/wall{$post->getPrettyId()}/delete">{_delete}</a>
|
||||||
<a class="profile_link" style="display:block;width:96%;" href="/report.pl/{$post->getId()}?type=post">{_report}</a>
|
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue