зеркало из https://github.com/mozilla/pjs.git
Content model changes to get the tree widget alive.
This commit is contained in:
Родитель
0dc62760dc
Коммит
853da1faac
|
@ -57,7 +57,7 @@ public:
|
||||||
* "content children," that is, a set of nsIRDFContent elements that
|
* "content children," that is, a set of nsIRDFContent elements that
|
||||||
* should appear as the node's children in the content model.
|
* should appear as the node's children in the content model.
|
||||||
*/
|
*/
|
||||||
NS_IMETHOD CreateChildren(nsIRDFContent* element, nsISupportsArray* children) = 0;
|
NS_IMETHOD CreateChildren(nsIRDFContent* element) = 0;
|
||||||
|
|
||||||
// XXX these should probably be strings so you can mess with them
|
// XXX these should probably be strings so you can mess with them
|
||||||
// via the DOM.
|
// via the DOM.
|
||||||
|
|
|
@ -1157,7 +1157,7 @@ nsRDFDocument::GetDataBase(nsIRDFDataBase*& result)
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsRDFDocument::CreateChildren(nsIRDFContent* element, nsISupportsArray* children)
|
nsRDFDocument::CreateChildren(nsIRDFContent* element)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
|
@ -1205,24 +1205,16 @@ nsRDFDocument::CreateChildren(nsIRDFContent* element, nsISupportsArray* children
|
||||||
PRBool moreValues;
|
PRBool moreValues;
|
||||||
while (NS_SUCCEEDED(rv = values->HasMoreElements(moreValues)) && moreValues) {
|
while (NS_SUCCEEDED(rv = values->HasMoreElements(moreValues)) && moreValues) {
|
||||||
nsIRDFNode* value = nsnull;
|
nsIRDFNode* value = nsnull;
|
||||||
if (NS_FAILED(rv = values->GetNext(value, tv /* ignored */)))
|
if (NS_SUCCEEDED(rv = values->GetNext(value, tv /* ignored */))) {
|
||||||
break;
|
// At this point, the specific nsRDFDocument
|
||||||
|
// implementations will create an appropriate child
|
||||||
// XXX At this point, we need to decide exactly what kind
|
// element (or elements).
|
||||||
// of kid to create in the content model. For example, for
|
rv = AddChild(element, property, value);
|
||||||
// leaf nodes, we probably want to create some kind of
|
|
||||||
// text element.
|
|
||||||
nsIRDFContent* child;
|
|
||||||
if (NS_FAILED(rv = CreateChild(property, value, child))) {
|
|
||||||
NS_RELEASE(value);
|
NS_RELEASE(value);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// And finally, add the child into the content model
|
if (NS_FAILED(rv))
|
||||||
children->AppendElement(child);
|
break;
|
||||||
|
|
||||||
NS_RELEASE(child);
|
|
||||||
NS_RELEASE(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_RELEASE(values);
|
NS_RELEASE(values);
|
||||||
|
|
|
@ -194,7 +194,7 @@ public:
|
||||||
NS_IMETHOD Init(void);
|
NS_IMETHOD Init(void);
|
||||||
NS_IMETHOD SetRootResource(nsIRDFNode* resource);
|
NS_IMETHOD SetRootResource(nsIRDFNode* resource);
|
||||||
NS_IMETHOD GetDataBase(nsIRDFDataBase*& result);
|
NS_IMETHOD GetDataBase(nsIRDFDataBase*& result);
|
||||||
NS_IMETHOD CreateChildren(nsIRDFContent* element, nsISupportsArray* children);
|
NS_IMETHOD CreateChildren(nsIRDFContent* element);
|
||||||
NS_IMETHOD AddTreeProperty(nsIRDFNode* resource);
|
NS_IMETHOD AddTreeProperty(nsIRDFNode* resource);
|
||||||
NS_IMETHOD RemoveTreeProperty(nsIRDFNode* resource);
|
NS_IMETHOD RemoveTreeProperty(nsIRDFNode* resource);
|
||||||
|
|
||||||
|
@ -214,9 +214,9 @@ protected:
|
||||||
nsIRDFContent*& result,
|
nsIRDFContent*& result,
|
||||||
PRBool childrenMustBeGenerated);
|
PRBool childrenMustBeGenerated);
|
||||||
|
|
||||||
virtual nsresult CreateChild(nsIRDFNode* property,
|
virtual nsresult AddChild(nsIRDFContent* parent,
|
||||||
nsIRDFNode* value,
|
nsIRDFNode* property,
|
||||||
nsIRDFContent*& result) = 0;
|
nsIRDFNode* value) = 0;
|
||||||
|
|
||||||
nsIArena* mArena;
|
nsIArena* mArena;
|
||||||
nsVoidArray mObservers;
|
nsVoidArray mObservers;
|
||||||
|
|
|
@ -1045,6 +1045,9 @@ nsRDFElement::GetAttributeCount(PRInt32& aResult) const
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
aResult = 0;
|
aResult = 0;
|
||||||
|
|
||||||
|
if (mAttributes)
|
||||||
|
aResult += mAttributes->Count();
|
||||||
|
|
||||||
#if defined(CREATE_PROPERTIES_AS_ATTRIBUTES)
|
#if defined(CREATE_PROPERTIES_AS_ATTRIBUTES)
|
||||||
PR_ASSERT(0); // XXX need to write this...
|
PR_ASSERT(0); // XXX need to write this...
|
||||||
#endif // defined(CREATE_PROPERTIES_AS_ATTRIBUTES)
|
#endif // defined(CREATE_PROPERTIES_AS_ATTRIBUTES)
|
||||||
|
@ -1062,9 +1065,6 @@ rdf_Indent(FILE* out, PRInt32 aIndent)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsRDFElement::List(FILE* out, PRInt32 aIndent) const
|
nsRDFElement::List(FILE* out, PRInt32 aIndent) const
|
||||||
{
|
{
|
||||||
if (! mResource)
|
|
||||||
return NS_ERROR_NOT_INITIALIZED;
|
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1219,9 +1219,12 @@ nsRDFElement::Init(nsIRDFDocument* doc,
|
||||||
PRBool childrenMustBeGenerated)
|
PRBool childrenMustBeGenerated)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(doc, "null ptr");
|
NS_PRECONDITION(doc, "null ptr");
|
||||||
NS_PRECONDITION(resource, "null ptr");
|
|
||||||
|
|
||||||
if (!doc || !resource)
|
// XXX Not using this because of a hack in nsRDFTreeDocument: need
|
||||||
|
// to expose generic XML elements!
|
||||||
|
//NS_PRECONDITION(resource, "null ptr");
|
||||||
|
|
||||||
|
if (!doc /* || !resource // XXX ibid */)
|
||||||
return NS_ERROR_NULL_POINTER;
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
|
||||||
mTag = aTag;
|
mTag = aTag;
|
||||||
|
@ -1229,15 +1232,13 @@ nsRDFElement::Init(nsIRDFDocument* doc,
|
||||||
mDocument = doc; // not refcounted
|
mDocument = doc; // not refcounted
|
||||||
|
|
||||||
mResource = resource;
|
mResource = resource;
|
||||||
NS_ADDREF(mResource);
|
NS_IF_ADDREF(mResource);
|
||||||
|
|
||||||
mChildrenMustBeGenerated = childrenMustBeGenerated;
|
mChildrenMustBeGenerated = childrenMustBeGenerated;
|
||||||
|
|
||||||
if (! childrenMustBeGenerated) {
|
nsresult rv;
|
||||||
nsresult rv;
|
if (NS_FAILED(rv = NS_NewISupportsArray(&mChildren)))
|
||||||
if (NS_FAILED(rv = NS_NewISupportsArray(&mChildren)))
|
return rv;
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -1383,11 +1384,17 @@ nsRDFElement::GenerateChildren(void) const
|
||||||
unconstThis->mChildren->Clear();
|
unconstThis->mChildren->Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_FAILED(rv = mDocument->CreateChildren(unconstThis,
|
// Clear this value *first*, so we can re-enter the nsIContent
|
||||||
unconstThis->mChildren)))
|
// getters if needed.
|
||||||
return rv;
|
|
||||||
|
|
||||||
unconstThis->mChildrenMustBeGenerated = PR_FALSE;
|
unconstThis->mChildrenMustBeGenerated = PR_FALSE;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = mDocument->CreateChildren(unconstThis))) {
|
||||||
|
// Well, maybe it was a transient error. This'll let use try
|
||||||
|
// again some time in the future.
|
||||||
|
unconstThis->mChildrenMustBeGenerated = PR_TRUE;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,9 @@ public:
|
||||||
virtual ~RDFHTMLDocumentImpl();
|
virtual ~RDFHTMLDocumentImpl();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual nsresult CreateChild(nsIRDFNode* property,
|
virtual nsresult AddChild(nsIRDFContent* parent,
|
||||||
nsIRDFNode* value,
|
nsIRDFNode* property,
|
||||||
nsIRDFContent*& result);
|
nsIRDFNode* value);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -59,9 +59,9 @@ RDFHTMLDocumentImpl::~RDFHTMLDocumentImpl(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RDFHTMLDocumentImpl::CreateChild(nsIRDFNode* property,
|
RDFHTMLDocumentImpl::AddChild(nsIRDFContent* parent,
|
||||||
nsIRDFNode* value,
|
nsIRDFNode* property,
|
||||||
nsIRDFContent*& result)
|
nsIRDFNode* value)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsIRDFContent* child = nsnull;
|
nsIRDFContent* child = nsnull;
|
||||||
|
@ -96,9 +96,6 @@ RDFHTMLDocumentImpl::CreateChild(nsIRDFNode* property,
|
||||||
|
|
||||||
if (NS_FAILED(rv = child->SetAttribute("ID", s, PR_FALSE)))
|
if (NS_FAILED(rv = child->SetAttribute("ID", s, PR_FALSE)))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
result = child;
|
|
||||||
NS_ADDREF(result);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Otherwise, it's not a tree property. So we'll just create a
|
// Otherwise, it's not a tree property. So we'll just create a
|
||||||
|
@ -115,11 +112,10 @@ RDFHTMLDocumentImpl::CreateChild(nsIRDFNode* property,
|
||||||
|
|
||||||
if (NS_FAILED(rv = AttachTextNode(child, value)))
|
if (NS_FAILED(rv = AttachTextNode(child, value)))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
result = child;
|
|
||||||
NS_ADDREF(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rv = parent->AppendChildTo(child, PR_TRUE);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
NS_IF_RELEASE(child);
|
NS_IF_RELEASE(child);
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
@ -29,15 +29,36 @@
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static const char* kChildrenTag = "CHILDREN";
|
||||||
|
static const char* kFolderTag = "FOLDER";
|
||||||
|
static const char* kItemTag = "ITEM";
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
class RDFTreeDocumentImpl : public nsRDFDocument {
|
class RDFTreeDocumentImpl : public nsRDFDocument {
|
||||||
public:
|
public:
|
||||||
RDFTreeDocumentImpl();
|
RDFTreeDocumentImpl();
|
||||||
virtual ~RDFTreeDocumentImpl();
|
virtual ~RDFTreeDocumentImpl();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual nsresult CreateChild(nsIRDFNode* property,
|
virtual nsresult
|
||||||
nsIRDFNode* value,
|
AddChild(nsIRDFContent* parent,
|
||||||
nsIRDFContent*& result);
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EnsureChildrenElement(nsIContent* parent,
|
||||||
|
nsIContent*& result);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
AddTreeChild(nsIRDFContent* parent,
|
||||||
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
AddPropertyChild(nsIRDFContent* parent,
|
||||||
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value);
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -50,70 +71,178 @@ RDFTreeDocumentImpl::~RDFTreeDocumentImpl(void)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
RDFTreeDocumentImpl::CreateChild(nsIRDFNode* property,
|
RDFTreeDocumentImpl::EnsureChildrenElement(nsIContent* parent,
|
||||||
nsIRDFNode* value,
|
nsIContent*& result)
|
||||||
nsIRDFContent*& result)
|
|
||||||
{
|
{
|
||||||
static const char* kTRTag = "TR";
|
nsresult rv;
|
||||||
static const char* kTDTag = "TD";
|
result = nsnull; // reasonable default
|
||||||
|
|
||||||
|
nsIAtom* childrenTag = NS_NewAtom(kChildrenTag);
|
||||||
|
if (! childrenTag)
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
|
||||||
|
PRInt32 count;
|
||||||
|
if (NS_FAILED(rv = parent->ChildCount(count))) {
|
||||||
|
NS_RELEASE(childrenTag);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PRInt32 i = 0; i < count; ++i) {
|
||||||
|
nsIContent* kid;
|
||||||
|
if (NS_FAILED(rv = parent->ChildAt(i, kid)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
nsIAtom* tag;
|
||||||
|
if (NS_FAILED(rv = parent->GetTag(tag))) {
|
||||||
|
NS_RELEASE(kid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == childrenTag) {
|
||||||
|
NS_RELEASE(tag);
|
||||||
|
NS_RELEASE(childrenTag);
|
||||||
|
result = kid; // no need to addref, got it from ChildAt().
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_RELEASE(tag);
|
||||||
|
NS_RELEASE(kid);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_RELEASE(childrenTag);
|
||||||
|
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
// if we get here, we need to construct a new <children> element.
|
||||||
|
|
||||||
|
// XXX this should be a generic XML element, not an nsRDFElement
|
||||||
|
nsIRDFContent* children = nsnull;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = NS_NewRDFElement(&children)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = children->Init(this, kChildrenTag, nsnull /* XXX */, PR_FALSE)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = parent->AppendChildTo(children, PR_FALSE)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
result = children;
|
||||||
|
NS_ADDREF(result);
|
||||||
|
|
||||||
|
done:
|
||||||
|
NS_IF_RELEASE(children);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
RDFTreeDocumentImpl::AddTreeChild(nsIRDFContent* parent,
|
||||||
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value)
|
||||||
|
{
|
||||||
|
// If it's a tree property, then we need to add the new child
|
||||||
|
// element to a special "children" element in the parent. The
|
||||||
|
// child element's value will be the value of the
|
||||||
|
// property. We'll also attach an "ID=" attribute to the new
|
||||||
|
// child; e.g.,
|
||||||
|
//
|
||||||
|
// <parent>
|
||||||
|
// ...
|
||||||
|
// <children>
|
||||||
|
// <item id="value">
|
||||||
|
// <!-- recursively generated -->
|
||||||
|
// </item>
|
||||||
|
// </children>
|
||||||
|
// ...
|
||||||
|
// </parent>
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
nsIRDFContent* child = nsnull;
|
nsIRDFContent* child = nsnull;
|
||||||
|
nsIContent* children = nsnull;
|
||||||
|
nsAutoString s;
|
||||||
|
|
||||||
if (IsTreeProperty(property) || rdf_IsContainer(mResourceMgr, mDB, value)) {
|
// Ensure that the <children> element exists on the parent.
|
||||||
// If it's a tree property, then create a child element whose
|
if (NS_FAILED(rv = EnsureChildrenElement(parent, children)))
|
||||||
// value is the value of the property. We'll also attach an "ID="
|
goto done;
|
||||||
// attribute to the new child; e.g.,
|
|
||||||
//
|
|
||||||
// <parent>
|
|
||||||
// <tr id="value">
|
|
||||||
// <!-- recursively generated -->
|
|
||||||
// </tr>
|
|
||||||
// ...
|
|
||||||
// </parent>
|
|
||||||
|
|
||||||
nsAutoString s;
|
// Create a new child that represents the "value"
|
||||||
|
// resource. PR_TRUE indicates that we want the child to
|
||||||
|
// dynamically generate its own kids.
|
||||||
|
if (NS_FAILED(rv = NewChild(kFolderTag, value, child, PR_TRUE)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
// PR_TRUE indicates that we want the child to dynamically
|
if (NS_FAILED(rv = value->GetStringValue(s)))
|
||||||
// generate its own kids.
|
goto done;
|
||||||
if (NS_FAILED(rv = NewChild(kTRTag, value, child, PR_TRUE)))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if (NS_FAILED(rv = value->GetStringValue(s)))
|
if (NS_FAILED(rv = child->SetAttribute("ID", s, PR_FALSE)))
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (NS_FAILED(rv = child->SetAttribute("ID", s, PR_FALSE)))
|
#define ALL_NODES_OPEN_HACK
|
||||||
goto done;
|
#ifdef ALL_NODES_OPEN_HACK
|
||||||
|
if (NS_FAILED(rv = child->SetAttribute("OPEN", "TRUE", PR_FALSE)))
|
||||||
|
goto done;
|
||||||
|
#endif
|
||||||
|
|
||||||
result = child;
|
// Finally, add the thing to the <children> element.
|
||||||
NS_ADDREF(result);
|
children->AppendChildTo(child, PR_TRUE);
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Otherwise, it's not a tree property. So we'll just create a
|
|
||||||
// new element for the property, and a simple text node for
|
|
||||||
// its value; e.g.,
|
|
||||||
//
|
|
||||||
// <parent>
|
|
||||||
// <td>value</td>
|
|
||||||
// ...
|
|
||||||
// </parent>
|
|
||||||
|
|
||||||
if (NS_FAILED(rv = NewChild(kTDTag, property, child, PR_FALSE)))
|
done:
|
||||||
goto done;
|
NS_IF_RELEASE(child);
|
||||||
|
NS_IF_RELEASE(children);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if (NS_FAILED(rv = AttachTextNode(child, value)))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
result = child;
|
nsresult
|
||||||
NS_ADDREF(result);
|
RDFTreeDocumentImpl::AddPropertyChild(nsIRDFContent* parent,
|
||||||
}
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value)
|
||||||
|
{
|
||||||
|
// Otherwise, it's not a tree property. So we'll just create a
|
||||||
|
// new element for the property, and a simple text node for
|
||||||
|
// its value; e.g.,
|
||||||
|
//
|
||||||
|
// <parent>
|
||||||
|
// <td>value</td>
|
||||||
|
// ...
|
||||||
|
// </parent>
|
||||||
|
nsresult rv;
|
||||||
|
nsIRDFContent* child = nsnull;
|
||||||
|
|
||||||
|
nsAutoString s;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = property->GetStringValue(s)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = NewChild(s, property, child, PR_FALSE)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (NS_FAILED(rv = AttachTextNode(child, value)))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
rv = parent->AppendChildTo(child, PR_TRUE);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
NS_IF_RELEASE(child);
|
NS_IF_RELEASE(child);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
RDFTreeDocumentImpl::AddChild(nsIRDFContent* parent,
|
||||||
|
nsIRDFNode* property,
|
||||||
|
nsIRDFNode* value)
|
||||||
|
{
|
||||||
|
if (IsTreeProperty(property) || rdf_IsContainer(mResourceMgr, mDB, value)) {
|
||||||
|
return AddTreeChild(parent, property, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return AddPropertyChild(parent, property, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче