Compare commits
1 Commits
litmus_mod
...
CVS
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5d6b83e4c |
617
mozilla/htmlparser/src/nsAVLTree.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nsAVLTree.h"
|
||||
|
||||
|
||||
enum eLean {eLeft,eNeutral,eRight};
|
||||
|
||||
struct NS_COM nsAVLNode {
|
||||
public:
|
||||
|
||||
nsAVLNode(void* aValue) {
|
||||
mLeft=0;
|
||||
mRight=0;
|
||||
mSkew=eNeutral;
|
||||
mValue=aValue;
|
||||
}
|
||||
|
||||
nsAVLNode* mLeft;
|
||||
nsAVLNode* mRight;
|
||||
eLean mSkew;
|
||||
void* mValue;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************
|
||||
Now begin the tree class. Don't forget that the comparison
|
||||
between nodes must occur via the comparitor function,
|
||||
otherwise all you're testing is pointer addresses.
|
||||
************************************************************/
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
nsAVLTree::nsAVLTree(nsAVLNodeComparitor& aComparitor,
|
||||
nsAVLNodeFunctor* aDeallocator) :
|
||||
mComparitor(aComparitor), mDeallocator(aDeallocator) {
|
||||
mRoot=0;
|
||||
mCount=0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
avlDeleteTree(nsAVLNode* aNode){
|
||||
if (aNode) {
|
||||
avlDeleteTree(aNode->mLeft);
|
||||
avlDeleteTree(aNode->mRight);
|
||||
delete aNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsAVLTree::~nsAVLTree(){
|
||||
if (mDeallocator) {
|
||||
ForEachDepthFirst(*mDeallocator);
|
||||
}
|
||||
avlDeleteTree(mRoot);
|
||||
}
|
||||
|
||||
|
||||
class CDoesntExist: public nsAVLNodeFunctor {
|
||||
public:
|
||||
CDoesntExist(const nsAVLTree& anotherTree) : mOtherTree(anotherTree) {
|
||||
}
|
||||
virtual void* operator()(void* anItem) {
|
||||
void* result=mOtherTree.FindItem(anItem);
|
||||
if(result)
|
||||
return nsnull;
|
||||
return anItem;
|
||||
}
|
||||
protected:
|
||||
const nsAVLTree& mOtherTree;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method compares two trees (members by identity).
|
||||
* @update gess12/27/98
|
||||
* @param tree to compare against
|
||||
* @return true if they are identical (contain same stuff).
|
||||
*/
|
||||
PRBool nsAVLTree::operator==(const nsAVLTree& aCopy) const{
|
||||
CDoesntExist functor(aCopy);
|
||||
void* theItem=FirstThat(functor);
|
||||
PRBool result=PRBool(!theItem);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlRotateRight(nsAVLNode*& aRootNode){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
|
||||
ptr2=aRootNode->mRight;
|
||||
if(ptr2->mSkew==eRight) {
|
||||
aRootNode->mRight=ptr2->mLeft;
|
||||
ptr2->mLeft=aRootNode;
|
||||
aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else {
|
||||
ptr3=ptr2->mLeft;
|
||||
ptr2->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=ptr2;
|
||||
aRootNode->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=aRootNode;
|
||||
if(ptr3->mSkew==eLeft)
|
||||
ptr2->mSkew=eRight;
|
||||
else ptr2->mSkew=eNeutral;
|
||||
if(ptr3->mSkew==eRight)
|
||||
aRootNode->mSkew=eLeft;
|
||||
else aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr3;
|
||||
}
|
||||
aRootNode->mSkew=eNeutral;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlRotateLeft(nsAVLNode*& aRootNode){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
|
||||
ptr2=aRootNode->mLeft;
|
||||
if(ptr2->mSkew==eLeft) {
|
||||
aRootNode->mLeft=ptr2->mRight;
|
||||
ptr2->mRight=aRootNode;
|
||||
aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else {
|
||||
ptr3=ptr2->mRight;
|
||||
ptr2->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=ptr2;
|
||||
aRootNode->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=aRootNode;
|
||||
if(ptr3->mSkew==eRight)
|
||||
ptr2->mSkew=eLeft;
|
||||
else ptr2->mSkew=eNeutral;
|
||||
if(ptr3->mSkew==eLeft)
|
||||
aRootNode->mSkew=eRight;
|
||||
else aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr3;
|
||||
}
|
||||
aRootNode->mSkew=eNeutral;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlInsert(nsAVLNode*& aRootNode, nsAVLNode* aNewNode,
|
||||
nsAVLNodeComparitor& aComparitor) {
|
||||
eAVLStatus result=eAVL_unknown;
|
||||
|
||||
if(!aRootNode) {
|
||||
aRootNode = aNewNode;
|
||||
return eAVL_ok;
|
||||
}
|
||||
|
||||
if(aNewNode==aRootNode->mValue) {
|
||||
return eAVL_duplicate;
|
||||
}
|
||||
|
||||
PRInt32 theCompareResult=aComparitor(aRootNode->mValue,aNewNode->mValue);
|
||||
if(0 < theCompareResult) { //if(anItem<aRootNode->mValue)
|
||||
result=avlInsert(aRootNode->mLeft,aNewNode,aComparitor);
|
||||
if(eAVL_ok==result) {
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
avlRotateLeft(aRootNode);
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eRight:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eLeft;
|
||||
break;
|
||||
} //switch
|
||||
}//if
|
||||
} //if
|
||||
else {
|
||||
result=avlInsert(aRootNode->mRight,aNewNode,aComparitor);
|
||||
if(eAVL_ok==result) {
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eRight:
|
||||
avlRotateRight(aRootNode);
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eRight;
|
||||
break;
|
||||
} //switch
|
||||
}
|
||||
} //if
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static void
|
||||
avlBalanceLeft(nsAVLNode*& aRootNode, PRBool& delOk){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
eLean balnc2;
|
||||
eLean balnc3;
|
||||
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
ptr2=aRootNode->mLeft;
|
||||
balnc2=ptr2->mSkew;
|
||||
if(balnc2!=eRight) {
|
||||
aRootNode->mLeft=ptr2->mRight;
|
||||
ptr2->mRight=aRootNode;
|
||||
if(balnc2==eNeutral){
|
||||
aRootNode->mSkew=eLeft;
|
||||
ptr2->mSkew=eRight;
|
||||
delOk=PR_FALSE;
|
||||
}
|
||||
else{
|
||||
aRootNode->mSkew=eNeutral;
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else{
|
||||
ptr3=ptr2->mRight;
|
||||
balnc3=ptr3->mSkew;
|
||||
ptr2->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=ptr2;
|
||||
aRootNode->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=aRootNode;
|
||||
if(balnc3==eRight) {
|
||||
ptr2->mSkew=eLeft;
|
||||
}
|
||||
else {
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
if(balnc3==eLeft) {
|
||||
aRootNode->mSkew=eRight;
|
||||
}
|
||||
else {
|
||||
aRootNode->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr3;
|
||||
ptr3->mSkew=eNeutral;
|
||||
}
|
||||
break;
|
||||
|
||||
case eRight:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
break;
|
||||
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eLeft;
|
||||
delOk=PR_FALSE;
|
||||
break;
|
||||
}//switch
|
||||
return;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static void
|
||||
avlBalanceRight(nsAVLNode*& aRootNode, PRBool& delOk){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
eLean balnc2;
|
||||
eLean balnc3;
|
||||
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
break;
|
||||
|
||||
case eRight:
|
||||
ptr2=aRootNode->mRight;
|
||||
balnc2=ptr2->mSkew;
|
||||
if(balnc2!=eLeft) {
|
||||
aRootNode->mRight=ptr2->mLeft;
|
||||
ptr2->mLeft=aRootNode;
|
||||
if(balnc2==eNeutral){
|
||||
aRootNode->mSkew=eRight;
|
||||
ptr2->mSkew=eLeft;
|
||||
delOk=PR_FALSE;
|
||||
}
|
||||
else{
|
||||
aRootNode->mSkew=eNeutral;
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else{
|
||||
ptr3=ptr2->mLeft;
|
||||
balnc3=ptr3->mSkew;
|
||||
ptr2->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=ptr2;
|
||||
aRootNode->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=aRootNode;
|
||||
if(balnc3==eLeft) {
|
||||
ptr2->mSkew=eRight;
|
||||
}
|
||||
else {
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
if(balnc3==eRight) {
|
||||
aRootNode->mSkew=eLeft;
|
||||
}
|
||||
else {
|
||||
aRootNode->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr3;
|
||||
ptr3->mSkew=eNeutral;
|
||||
}
|
||||
break;
|
||||
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eRight;
|
||||
delOk=PR_FALSE;
|
||||
break;
|
||||
}//switch
|
||||
return;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlRemoveChildren(nsAVLNode*& aRootNode,nsAVLNode*& anotherNode, PRBool& delOk){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
if(!anotherNode->mRight){
|
||||
aRootNode->mValue=anotherNode->mValue; //swap
|
||||
anotherNode=anotherNode->mLeft;
|
||||
delOk=PR_TRUE;
|
||||
}
|
||||
else{
|
||||
avlRemoveChildren(aRootNode,anotherNode->mRight,delOk);
|
||||
if(delOk)
|
||||
avlBalanceLeft(anotherNode,delOk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlRemove(nsAVLNode*& aRootNode, void* anItem, PRBool& delOk,
|
||||
nsAVLNodeComparitor& aComparitor){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
if(!aRootNode)
|
||||
delOk=PR_FALSE;
|
||||
else {
|
||||
PRInt32 cmp=aComparitor(anItem,aRootNode->mValue);
|
||||
if(cmp<0){
|
||||
avlRemove(aRootNode->mLeft,anItem,delOk,aComparitor);
|
||||
if(delOk)
|
||||
avlBalanceRight(aRootNode,delOk);
|
||||
}
|
||||
else if(cmp>0){
|
||||
avlRemove(aRootNode->mRight,anItem,delOk,aComparitor);
|
||||
if(delOk)
|
||||
avlBalanceLeft(aRootNode,delOk);
|
||||
}
|
||||
else{ //they match...
|
||||
nsAVLNode* temp=aRootNode;
|
||||
if(!aRootNode->mRight) {
|
||||
aRootNode=aRootNode->mLeft;
|
||||
delOk=PR_TRUE;
|
||||
delete temp;
|
||||
}
|
||||
else if(!aRootNode->mLeft) {
|
||||
aRootNode=aRootNode->mRight;
|
||||
delOk=PR_TRUE;
|
||||
delete temp;
|
||||
}
|
||||
else {
|
||||
avlRemoveChildren(aRootNode,aRootNode->mLeft,delOk);
|
||||
if(delOk)
|
||||
avlBalanceRight(aRootNode,delOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
eAVLStatus
|
||||
nsAVLTree::AddItem(void* anItem){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
nsAVLNode* theNewNode=new nsAVLNode(anItem);
|
||||
result=avlInsert(mRoot,theNewNode,mComparitor);
|
||||
if(eAVL_duplicate!=result)
|
||||
mCount++;
|
||||
else {
|
||||
delete theNewNode;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
void* nsAVLTree::FindItem(void* aValue) const{
|
||||
nsAVLNode* result=mRoot;
|
||||
PRInt32 count=0;
|
||||
while(result) {
|
||||
count++;
|
||||
PRInt32 cmp=mComparitor(aValue,result->mValue);
|
||||
if(0==cmp) {
|
||||
//we matched...
|
||||
break;
|
||||
}
|
||||
else if(0>cmp){
|
||||
//theNode was greater...
|
||||
result=result->mLeft;
|
||||
}
|
||||
else {
|
||||
//aValue is greater...
|
||||
result=result->mRight;
|
||||
}
|
||||
}
|
||||
if(result) {
|
||||
return result->mValue;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/30/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
eAVLStatus
|
||||
nsAVLTree::RemoveItem(void* aValue){
|
||||
PRBool delOk=PR_TRUE;
|
||||
eAVLStatus result=avlRemove(mRoot,aValue,delOk,mComparitor);
|
||||
if(eAVL_ok==result)
|
||||
mCount--;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlForEachDepthFirst(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor){
|
||||
if(aNode) {
|
||||
avlForEachDepthFirst(aNode->mLeft,aFunctor);
|
||||
avlForEachDepthFirst(aNode->mRight,aFunctor);
|
||||
aFunctor(aNode->mValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void
|
||||
nsAVLTree::ForEachDepthFirst(nsAVLNodeFunctor& aFunctor) const{
|
||||
::avlForEachDepthFirst(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlForEach(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor) {
|
||||
if(aNode) {
|
||||
avlForEach(aNode->mLeft,aFunctor);
|
||||
aFunctor(aNode->mValue);
|
||||
avlForEach(aNode->mRight,aFunctor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void
|
||||
nsAVLTree::ForEach(nsAVLNodeFunctor& aFunctor) const{
|
||||
::avlForEach(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void*
|
||||
avlFirstThat(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor) {
|
||||
void* result=nsnull;
|
||||
if(aNode) {
|
||||
result = avlFirstThat(aNode->mLeft,aFunctor);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = aFunctor(aNode->mValue);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = avlFirstThat(aNode->mRight,aFunctor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void*
|
||||
nsAVLTree::FirstThat(nsAVLNodeFunctor& aFunctor) const{
|
||||
return ::avlFirstThat(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
74
mozilla/htmlparser/src/nsAVLTree.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsAVLTree_h___
|
||||
#define nsAVLTree_h___
|
||||
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
|
||||
enum eAVLStatus {eAVL_unknown,eAVL_ok,eAVL_fail,eAVL_duplicate};
|
||||
|
||||
|
||||
struct nsAVLNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/26/98
|
||||
* @param anObject1 is the first object to be compared
|
||||
* @param anObject2 is the second object to be compared
|
||||
* @return -1,0,1 if object1 is less, equal, greater than object2
|
||||
*/
|
||||
class NS_COM nsAVLNodeComparitor {
|
||||
public:
|
||||
virtual PRInt32 operator()(void* anItem1,void* anItem2)=0;
|
||||
};
|
||||
|
||||
class NS_COM nsAVLNodeFunctor {
|
||||
public:
|
||||
virtual void* operator()(void* anItem)=0;
|
||||
};
|
||||
|
||||
class NS_COM nsAVLTree {
|
||||
public:
|
||||
nsAVLTree(nsAVLNodeComparitor& aComparitor, nsAVLNodeFunctor* aDeallocator);
|
||||
~nsAVLTree(void);
|
||||
|
||||
PRBool operator==(const nsAVLTree& aOther) const;
|
||||
PRInt32 GetCount(void) const {return mCount;}
|
||||
|
||||
//main functions...
|
||||
eAVLStatus AddItem(void* anItem);
|
||||
eAVLStatus RemoveItem(void* anItem);
|
||||
void* FindItem(void* anItem) const;
|
||||
void ForEach(nsAVLNodeFunctor& aFunctor) const;
|
||||
void ForEachDepthFirst(nsAVLNodeFunctor& aFunctor) const;
|
||||
void* FirstThat(nsAVLNodeFunctor& aFunctor) const;
|
||||
|
||||
protected:
|
||||
|
||||
nsAVLNode* mRoot;
|
||||
PRInt32 mCount;
|
||||
nsAVLNodeComparitor& mComparitor;
|
||||
nsAVLNodeFunctor* mDeallocator;
|
||||
};
|
||||
|
||||
|
||||
#endif /* nsAVLTree_h___ */
|
||||
|
||||
617
mozilla/parser/htmlparser/src/nsAVLTree.cpp
Normal file
@@ -0,0 +1,617 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nsAVLTree.h"
|
||||
|
||||
|
||||
enum eLean {eLeft,eNeutral,eRight};
|
||||
|
||||
struct NS_COM nsAVLNode {
|
||||
public:
|
||||
|
||||
nsAVLNode(void* aValue) {
|
||||
mLeft=0;
|
||||
mRight=0;
|
||||
mSkew=eNeutral;
|
||||
mValue=aValue;
|
||||
}
|
||||
|
||||
nsAVLNode* mLeft;
|
||||
nsAVLNode* mRight;
|
||||
eLean mSkew;
|
||||
void* mValue;
|
||||
};
|
||||
|
||||
|
||||
/************************************************************
|
||||
Now begin the tree class. Don't forget that the comparison
|
||||
between nodes must occur via the comparitor function,
|
||||
otherwise all you're testing is pointer addresses.
|
||||
************************************************************/
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
nsAVLTree::nsAVLTree(nsAVLNodeComparitor& aComparitor,
|
||||
nsAVLNodeFunctor* aDeallocator) :
|
||||
mComparitor(aComparitor), mDeallocator(aDeallocator) {
|
||||
mRoot=0;
|
||||
mCount=0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
avlDeleteTree(nsAVLNode* aNode){
|
||||
if (aNode) {
|
||||
avlDeleteTree(aNode->mLeft);
|
||||
avlDeleteTree(aNode->mRight);
|
||||
delete aNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
nsAVLTree::~nsAVLTree(){
|
||||
if (mDeallocator) {
|
||||
ForEachDepthFirst(*mDeallocator);
|
||||
}
|
||||
avlDeleteTree(mRoot);
|
||||
}
|
||||
|
||||
|
||||
class CDoesntExist: public nsAVLNodeFunctor {
|
||||
public:
|
||||
CDoesntExist(const nsAVLTree& anotherTree) : mOtherTree(anotherTree) {
|
||||
}
|
||||
virtual void* operator()(void* anItem) {
|
||||
void* result=mOtherTree.FindItem(anItem);
|
||||
if(result)
|
||||
return nsnull;
|
||||
return anItem;
|
||||
}
|
||||
protected:
|
||||
const nsAVLTree& mOtherTree;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method compares two trees (members by identity).
|
||||
* @update gess12/27/98
|
||||
* @param tree to compare against
|
||||
* @return true if they are identical (contain same stuff).
|
||||
*/
|
||||
PRBool nsAVLTree::operator==(const nsAVLTree& aCopy) const{
|
||||
CDoesntExist functor(aCopy);
|
||||
void* theItem=FirstThat(functor);
|
||||
PRBool result=PRBool(!theItem);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlRotateRight(nsAVLNode*& aRootNode){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
|
||||
ptr2=aRootNode->mRight;
|
||||
if(ptr2->mSkew==eRight) {
|
||||
aRootNode->mRight=ptr2->mLeft;
|
||||
ptr2->mLeft=aRootNode;
|
||||
aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else {
|
||||
ptr3=ptr2->mLeft;
|
||||
ptr2->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=ptr2;
|
||||
aRootNode->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=aRootNode;
|
||||
if(ptr3->mSkew==eLeft)
|
||||
ptr2->mSkew=eRight;
|
||||
else ptr2->mSkew=eNeutral;
|
||||
if(ptr3->mSkew==eRight)
|
||||
aRootNode->mSkew=eLeft;
|
||||
else aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr3;
|
||||
}
|
||||
aRootNode->mSkew=eNeutral;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/27/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlRotateLeft(nsAVLNode*& aRootNode){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
|
||||
ptr2=aRootNode->mLeft;
|
||||
if(ptr2->mSkew==eLeft) {
|
||||
aRootNode->mLeft=ptr2->mRight;
|
||||
ptr2->mRight=aRootNode;
|
||||
aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else {
|
||||
ptr3=ptr2->mRight;
|
||||
ptr2->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=ptr2;
|
||||
aRootNode->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=aRootNode;
|
||||
if(ptr3->mSkew==eRight)
|
||||
ptr2->mSkew=eLeft;
|
||||
else ptr2->mSkew=eNeutral;
|
||||
if(ptr3->mSkew==eLeft)
|
||||
aRootNode->mSkew=eRight;
|
||||
else aRootNode->mSkew=eNeutral;
|
||||
aRootNode=ptr3;
|
||||
}
|
||||
aRootNode->mSkew=eNeutral;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlInsert(nsAVLNode*& aRootNode, nsAVLNode* aNewNode,
|
||||
nsAVLNodeComparitor& aComparitor) {
|
||||
eAVLStatus result=eAVL_unknown;
|
||||
|
||||
if(!aRootNode) {
|
||||
aRootNode = aNewNode;
|
||||
return eAVL_ok;
|
||||
}
|
||||
|
||||
if(aNewNode==aRootNode->mValue) {
|
||||
return eAVL_duplicate;
|
||||
}
|
||||
|
||||
PRInt32 theCompareResult=aComparitor(aRootNode->mValue,aNewNode->mValue);
|
||||
if(0 < theCompareResult) { //if(anItem<aRootNode->mValue)
|
||||
result=avlInsert(aRootNode->mLeft,aNewNode,aComparitor);
|
||||
if(eAVL_ok==result) {
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
avlRotateLeft(aRootNode);
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eRight:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eLeft;
|
||||
break;
|
||||
} //switch
|
||||
}//if
|
||||
} //if
|
||||
else {
|
||||
result=avlInsert(aRootNode->mRight,aNewNode,aComparitor);
|
||||
if(eAVL_ok==result) {
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eRight:
|
||||
avlRotateRight(aRootNode);
|
||||
result=eAVL_fail;
|
||||
break;
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eRight;
|
||||
break;
|
||||
} //switch
|
||||
}
|
||||
} //if
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static void
|
||||
avlBalanceLeft(nsAVLNode*& aRootNode, PRBool& delOk){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
eLean balnc2;
|
||||
eLean balnc3;
|
||||
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
ptr2=aRootNode->mLeft;
|
||||
balnc2=ptr2->mSkew;
|
||||
if(balnc2!=eRight) {
|
||||
aRootNode->mLeft=ptr2->mRight;
|
||||
ptr2->mRight=aRootNode;
|
||||
if(balnc2==eNeutral){
|
||||
aRootNode->mSkew=eLeft;
|
||||
ptr2->mSkew=eRight;
|
||||
delOk=PR_FALSE;
|
||||
}
|
||||
else{
|
||||
aRootNode->mSkew=eNeutral;
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else{
|
||||
ptr3=ptr2->mRight;
|
||||
balnc3=ptr3->mSkew;
|
||||
ptr2->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=ptr2;
|
||||
aRootNode->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=aRootNode;
|
||||
if(balnc3==eRight) {
|
||||
ptr2->mSkew=eLeft;
|
||||
}
|
||||
else {
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
if(balnc3==eLeft) {
|
||||
aRootNode->mSkew=eRight;
|
||||
}
|
||||
else {
|
||||
aRootNode->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr3;
|
||||
ptr3->mSkew=eNeutral;
|
||||
}
|
||||
break;
|
||||
|
||||
case eRight:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
break;
|
||||
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eLeft;
|
||||
delOk=PR_FALSE;
|
||||
break;
|
||||
}//switch
|
||||
return;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static void
|
||||
avlBalanceRight(nsAVLNode*& aRootNode, PRBool& delOk){
|
||||
nsAVLNode* ptr2;
|
||||
nsAVLNode* ptr3;
|
||||
eLean balnc2;
|
||||
eLean balnc3;
|
||||
|
||||
switch(aRootNode->mSkew){
|
||||
case eLeft:
|
||||
aRootNode->mSkew=eNeutral;
|
||||
break;
|
||||
|
||||
case eRight:
|
||||
ptr2=aRootNode->mRight;
|
||||
balnc2=ptr2->mSkew;
|
||||
if(balnc2!=eLeft) {
|
||||
aRootNode->mRight=ptr2->mLeft;
|
||||
ptr2->mLeft=aRootNode;
|
||||
if(balnc2==eNeutral){
|
||||
aRootNode->mSkew=eRight;
|
||||
ptr2->mSkew=eLeft;
|
||||
delOk=PR_FALSE;
|
||||
}
|
||||
else{
|
||||
aRootNode->mSkew=eNeutral;
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr2;
|
||||
}
|
||||
else{
|
||||
ptr3=ptr2->mLeft;
|
||||
balnc3=ptr3->mSkew;
|
||||
ptr2->mLeft=ptr3->mRight;
|
||||
ptr3->mRight=ptr2;
|
||||
aRootNode->mRight=ptr3->mLeft;
|
||||
ptr3->mLeft=aRootNode;
|
||||
if(balnc3==eLeft) {
|
||||
ptr2->mSkew=eRight;
|
||||
}
|
||||
else {
|
||||
ptr2->mSkew=eNeutral;
|
||||
}
|
||||
if(balnc3==eRight) {
|
||||
aRootNode->mSkew=eLeft;
|
||||
}
|
||||
else {
|
||||
aRootNode->mSkew=eNeutral;
|
||||
}
|
||||
aRootNode=ptr3;
|
||||
ptr3->mSkew=eNeutral;
|
||||
}
|
||||
break;
|
||||
|
||||
case eNeutral:
|
||||
aRootNode->mSkew=eRight;
|
||||
delOk=PR_FALSE;
|
||||
break;
|
||||
}//switch
|
||||
return;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlRemoveChildren(nsAVLNode*& aRootNode,nsAVLNode*& anotherNode, PRBool& delOk){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
if(!anotherNode->mRight){
|
||||
aRootNode->mValue=anotherNode->mValue; //swap
|
||||
anotherNode=anotherNode->mLeft;
|
||||
delOk=PR_TRUE;
|
||||
}
|
||||
else{
|
||||
avlRemoveChildren(aRootNode,anotherNode->mRight,delOk);
|
||||
if(delOk)
|
||||
avlBalanceLeft(anotherNode,delOk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
static eAVLStatus
|
||||
avlRemove(nsAVLNode*& aRootNode, void* anItem, PRBool& delOk,
|
||||
nsAVLNodeComparitor& aComparitor){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
if(!aRootNode)
|
||||
delOk=PR_FALSE;
|
||||
else {
|
||||
PRInt32 cmp=aComparitor(anItem,aRootNode->mValue);
|
||||
if(cmp<0){
|
||||
avlRemove(aRootNode->mLeft,anItem,delOk,aComparitor);
|
||||
if(delOk)
|
||||
avlBalanceRight(aRootNode,delOk);
|
||||
}
|
||||
else if(cmp>0){
|
||||
avlRemove(aRootNode->mRight,anItem,delOk,aComparitor);
|
||||
if(delOk)
|
||||
avlBalanceLeft(aRootNode,delOk);
|
||||
}
|
||||
else{ //they match...
|
||||
nsAVLNode* temp=aRootNode;
|
||||
if(!aRootNode->mRight) {
|
||||
aRootNode=aRootNode->mLeft;
|
||||
delOk=PR_TRUE;
|
||||
delete temp;
|
||||
}
|
||||
else if(!aRootNode->mLeft) {
|
||||
aRootNode=aRootNode->mRight;
|
||||
delOk=PR_TRUE;
|
||||
delete temp;
|
||||
}
|
||||
else {
|
||||
avlRemoveChildren(aRootNode,aRootNode->mLeft,delOk);
|
||||
if(delOk)
|
||||
avlBalanceRight(aRootNode,delOk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
eAVLStatus
|
||||
nsAVLTree::AddItem(void* anItem){
|
||||
eAVLStatus result=eAVL_ok;
|
||||
|
||||
nsAVLNode* theNewNode=new nsAVLNode(anItem);
|
||||
result=avlInsert(mRoot,theNewNode,mComparitor);
|
||||
if(eAVL_duplicate!=result)
|
||||
mCount++;
|
||||
else {
|
||||
delete theNewNode;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** ------------------------------------------------
|
||||
*
|
||||
*
|
||||
* @update gess 4/22/98
|
||||
* @param
|
||||
* @return
|
||||
*/ //----------------------------------------------
|
||||
void* nsAVLTree::FindItem(void* aValue) const{
|
||||
nsAVLNode* result=mRoot;
|
||||
PRInt32 count=0;
|
||||
while(result) {
|
||||
count++;
|
||||
PRInt32 cmp=mComparitor(aValue,result->mValue);
|
||||
if(0==cmp) {
|
||||
//we matched...
|
||||
break;
|
||||
}
|
||||
else if(0>cmp){
|
||||
//theNode was greater...
|
||||
result=result->mLeft;
|
||||
}
|
||||
else {
|
||||
//aValue is greater...
|
||||
result=result->mRight;
|
||||
}
|
||||
}
|
||||
if(result) {
|
||||
return result->mValue;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/30/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
eAVLStatus
|
||||
nsAVLTree::RemoveItem(void* aValue){
|
||||
PRBool delOk=PR_TRUE;
|
||||
eAVLStatus result=avlRemove(mRoot,aValue,delOk,mComparitor);
|
||||
if(eAVL_ok==result)
|
||||
mCount--;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlForEachDepthFirst(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor){
|
||||
if(aNode) {
|
||||
avlForEachDepthFirst(aNode->mLeft,aFunctor);
|
||||
avlForEachDepthFirst(aNode->mRight,aFunctor);
|
||||
aFunctor(aNode->mValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void
|
||||
nsAVLTree::ForEachDepthFirst(nsAVLNodeFunctor& aFunctor) const{
|
||||
::avlForEachDepthFirst(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void
|
||||
avlForEach(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor) {
|
||||
if(aNode) {
|
||||
avlForEach(aNode->mLeft,aFunctor);
|
||||
aFunctor(aNode->mValue);
|
||||
avlForEach(aNode->mRight,aFunctor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void
|
||||
nsAVLTree::ForEach(nsAVLNodeFunctor& aFunctor) const{
|
||||
::avlForEach(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
static void*
|
||||
avlFirstThat(nsAVLNode* aNode, nsAVLNodeFunctor& aFunctor) {
|
||||
void* result=nsnull;
|
||||
if(aNode) {
|
||||
result = avlFirstThat(aNode->mLeft,aFunctor);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = aFunctor(aNode->mValue);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
result = avlFirstThat(aNode->mRight,aFunctor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess9/11/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void*
|
||||
nsAVLTree::FirstThat(nsAVLNodeFunctor& aFunctor) const{
|
||||
return ::avlFirstThat(mRoot,aFunctor);
|
||||
}
|
||||
|
||||
74
mozilla/parser/htmlparser/src/nsAVLTree.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#ifndef nsAVLTree_h___
|
||||
#define nsAVLTree_h___
|
||||
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
|
||||
enum eAVLStatus {eAVL_unknown,eAVL_ok,eAVL_fail,eAVL_duplicate};
|
||||
|
||||
|
||||
struct nsAVLNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess12/26/98
|
||||
* @param anObject1 is the first object to be compared
|
||||
* @param anObject2 is the second object to be compared
|
||||
* @return -1,0,1 if object1 is less, equal, greater than object2
|
||||
*/
|
||||
class NS_COM nsAVLNodeComparitor {
|
||||
public:
|
||||
virtual PRInt32 operator()(void* anItem1,void* anItem2)=0;
|
||||
};
|
||||
|
||||
class NS_COM nsAVLNodeFunctor {
|
||||
public:
|
||||
virtual void* operator()(void* anItem)=0;
|
||||
};
|
||||
|
||||
class NS_COM nsAVLTree {
|
||||
public:
|
||||
nsAVLTree(nsAVLNodeComparitor& aComparitor, nsAVLNodeFunctor* aDeallocator);
|
||||
~nsAVLTree(void);
|
||||
|
||||
PRBool operator==(const nsAVLTree& aOther) const;
|
||||
PRInt32 GetCount(void) const {return mCount;}
|
||||
|
||||
//main functions...
|
||||
eAVLStatus AddItem(void* anItem);
|
||||
eAVLStatus RemoveItem(void* anItem);
|
||||
void* FindItem(void* anItem) const;
|
||||
void ForEach(nsAVLNodeFunctor& aFunctor) const;
|
||||
void ForEachDepthFirst(nsAVLNodeFunctor& aFunctor) const;
|
||||
void* FirstThat(nsAVLNodeFunctor& aFunctor) const;
|
||||
|
||||
protected:
|
||||
|
||||
nsAVLNode* mRoot;
|
||||
PRInt32 mCount;
|
||||
nsAVLNodeComparitor& mComparitor;
|
||||
nsAVLNodeFunctor* mDeallocator;
|
||||
};
|
||||
|
||||
|
||||
#endif /* nsAVLTree_h___ */
|
||||
|
||||
717
mozilla/string/obsolete/nsStr.cpp
Normal file
@@ -0,0 +1,717 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/******************************************************************************************
|
||||
MODULE NOTES:
|
||||
|
||||
This file contains the nsStr data structure.
|
||||
This general purpose buffer management class is used as the basis for our strings.
|
||||
It's benefits include:
|
||||
1. An efficient set of library style functions for manipulating nsStrs
|
||||
2. Support for 1 and 2 byte character strings (which can easily be increased to n)
|
||||
3. Unicode awareness and interoperability.
|
||||
|
||||
*******************************************************************************************/
|
||||
|
||||
#include "nsStr.h"
|
||||
#include "bufferRoutines.h"
|
||||
#include "stdio.h" //only used for printf
|
||||
#include "nsCRT.h"
|
||||
#include "nsDeque.h"
|
||||
|
||||
|
||||
//static const char* kCallFindChar = "For better performance, call FindChar() for targets whose length==1.";
|
||||
//static const char* kCallRFindChar = "For better performance, call RFindChar() for targets whose length==1.";
|
||||
|
||||
static const PRUnichar gCommonEmptyBuffer[1] = {0};
|
||||
|
||||
/**
|
||||
* This method initializes all the members of the nsStr structure
|
||||
*
|
||||
* @update gess10/30/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::Initialize(nsStr& aDest,eCharSize aCharSize) {
|
||||
aDest.mStr=(char*)gCommonEmptyBuffer;
|
||||
aDest.mLength=0;
|
||||
aDest.mCapacity=0;
|
||||
aDest.mCharSize=aCharSize;
|
||||
aDest.mOwnsBuffer=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method initializes all the members of the nsStr structure
|
||||
* @update gess10/30/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::Initialize(nsStr& aDest,char* aCString,PRUint32 aCapacity,PRUint32 aLength,eCharSize aCharSize,PRBool aOwnsBuffer){
|
||||
aDest.mStr=(aCString) ? aCString : (char*)gCommonEmptyBuffer;
|
||||
aDest.mLength=aLength;
|
||||
aDest.mCapacity=aCapacity;
|
||||
aDest.mCharSize=aCharSize;
|
||||
aDest.mOwnsBuffer=aOwnsBuffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This member destroys the memory buffer owned by an nsStr object (if it actually owns it)
|
||||
* @update gess10/30/98
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::Destroy(nsStr& aDest) {
|
||||
if((aDest.mStr) && (aDest.mStr!=(char*)gCommonEmptyBuffer)) {
|
||||
Free(aDest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method gets called when the internal buffer needs
|
||||
* to grow to a given size. The original contents are not preserved.
|
||||
* @update gess 3/30/98
|
||||
* @param aNewLength -- new capacity of string in charSize units
|
||||
* @return void
|
||||
*/
|
||||
PRBool nsStr::EnsureCapacity(nsStr& aString,PRUint32 aNewLength) {
|
||||
PRBool result=PR_TRUE;
|
||||
if(aNewLength>aString.mCapacity) {
|
||||
result=Realloc(aString,aNewLength);
|
||||
if(aString.mStr)
|
||||
AddNullTerminator(aString);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called when the internal buffer needs
|
||||
* to grow to a given size. The original contents ARE preserved.
|
||||
* @update gess 3/30/98
|
||||
* @param aNewLength -- new capacity of string in charSize units
|
||||
* @return void
|
||||
*/
|
||||
PRBool nsStr::GrowCapacity(nsStr& aDest,PRUint32 aNewLength) {
|
||||
PRBool result=PR_TRUE;
|
||||
if(aNewLength>aDest.mCapacity) {
|
||||
nsStr theTempStr;
|
||||
nsStr::Initialize(theTempStr,aDest.mCharSize);
|
||||
|
||||
result=EnsureCapacity(theTempStr,aNewLength);
|
||||
if(result) {
|
||||
if(aDest.mLength) {
|
||||
Append(theTempStr,aDest,0,aDest.mLength);
|
||||
}
|
||||
Free(aDest);
|
||||
aDest.mStr = theTempStr.mStr;
|
||||
theTempStr.mStr=0; //make sure to null this out so that you don't lose the buffer you just stole...
|
||||
aDest.mLength=theTempStr.mLength;
|
||||
aDest.mCapacity=theTempStr.mCapacity;
|
||||
aDest.mOwnsBuffer=theTempStr.mOwnsBuffer;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the contents of aDest with aSource, up to aCount of chars.
|
||||
* @update gess10/30/98
|
||||
* @param aDest is the nsStr that gets changed.
|
||||
* @param aSource is where chars are copied from
|
||||
* @param aCount is the number of chars copied from aSource
|
||||
*/
|
||||
void nsStr::Assign(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount){
|
||||
if(&aDest!=&aSource){
|
||||
Truncate(aDest,0);
|
||||
Append(aDest,aSource,anOffset,aCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method appends the given nsStr to this one. Note that we have to
|
||||
* pay attention to the underlying char-size of both structs.
|
||||
* @update gess10/30/98
|
||||
* @param aDest is the nsStr to be manipulated
|
||||
* @param aSource is where char are copied from
|
||||
* @aCount is the number of bytes to be copied
|
||||
*/
|
||||
void nsStr::Append(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount){
|
||||
if(anOffset<aSource.mLength){
|
||||
PRUint32 theRealLen=(aCount<0) ? aSource.mLength : MinInt(aCount,aSource.mLength);
|
||||
PRUint32 theLength=(anOffset+theRealLen<aSource.mLength) ? theRealLen : (aSource.mLength-anOffset);
|
||||
if(0<theLength){
|
||||
|
||||
PRBool isBigEnough=PR_TRUE;
|
||||
if(aDest.mLength+theLength > aDest.mCapacity) {
|
||||
isBigEnough=GrowCapacity(aDest,aDest.mLength+theLength);
|
||||
}
|
||||
|
||||
if(isBigEnough) {
|
||||
//now append new chars, starting at offset
|
||||
(*gCopyChars[aSource.mCharSize][aDest.mCharSize])(aDest.mStr,aDest.mLength,aSource.mStr,anOffset,theLength);
|
||||
|
||||
aDest.mLength+=theLength;
|
||||
AddNullTerminator(aDest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method inserts up to "aCount" chars from a source nsStr into a dest nsStr.
|
||||
* @update gess10/30/98
|
||||
* @param aDest is the nsStr that gets changed
|
||||
* @param aDestOffset is where in aDest the insertion is to occur
|
||||
* @param aSource is where chars are copied from
|
||||
* @param aSrcOffset is where in aSource chars are copied from
|
||||
* @param aCount is the number of chars from aSource to be inserted into aDest
|
||||
*/
|
||||
void nsStr::Insert( nsStr& aDest,PRUint32 aDestOffset,const nsStr& aSource,PRUint32 aSrcOffset,PRInt32 aCount){
|
||||
//there are a few cases for insert:
|
||||
// 1. You're inserting chars into an empty string (assign)
|
||||
// 2. You're inserting onto the end of a string (append)
|
||||
// 3. You're inserting onto the 1..n-1 pos of a string (the hard case).
|
||||
if(0<aSource.mLength){
|
||||
if(aDest.mLength){
|
||||
if(aDestOffset<aDest.mLength){
|
||||
PRInt32 theRealLen=(aCount<0) ? aSource.mLength : MinInt(aCount,aSource.mLength);
|
||||
PRInt32 theLength=(aSrcOffset+theRealLen<aSource.mLength) ? theRealLen : (aSource.mLength-aSrcOffset);
|
||||
|
||||
if(aSrcOffset<aSource.mLength) {
|
||||
//here's the only new case we have to handle.
|
||||
//chars are really being inserted into our buffer...
|
||||
|
||||
if(aDest.mLength+theLength > aDest.mCapacity) {
|
||||
nsStr theTempStr;
|
||||
nsStr::Initialize(theTempStr,aDest.mCharSize);
|
||||
|
||||
PRBool isBigEnough=EnsureCapacity(theTempStr,aDest.mLength+theLength); //grow the temp buffer to the right size
|
||||
|
||||
if(isBigEnough) {
|
||||
if(aDestOffset) {
|
||||
Append(theTempStr,aDest,0,aDestOffset); //first copy leftmost data...
|
||||
}
|
||||
|
||||
Append(theTempStr,aSource,0,aSource.mLength); //next copy inserted (new) data
|
||||
|
||||
PRUint32 theRemains=aDest.mLength-aDestOffset;
|
||||
if(theRemains) {
|
||||
Append(theTempStr,aDest,aDestOffset,theRemains); //next copy rightmost data
|
||||
}
|
||||
|
||||
Free(aDest);
|
||||
aDest.mStr = theTempStr.mStr;
|
||||
theTempStr.mStr=0; //make sure to null this out so that you don't lose the buffer you just stole...
|
||||
aDest.mCapacity=theTempStr.mCapacity;
|
||||
aDest.mOwnsBuffer=theTempStr.mOwnsBuffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
//shift the chars right by theDelta...
|
||||
(*gShiftChars[aDest.mCharSize][KSHIFTRIGHT])(aDest.mStr,aDest.mLength,aDestOffset,theLength);
|
||||
|
||||
//now insert new chars, starting at offset
|
||||
(*gCopyChars[aSource.mCharSize][aDest.mCharSize])(aDest.mStr,aDestOffset,aSource.mStr,aSrcOffset,theLength);
|
||||
}
|
||||
|
||||
//finally, make sure to update the string length...
|
||||
aDest.mLength+=theLength;
|
||||
AddNullTerminator(aDest);
|
||||
|
||||
}//if
|
||||
//else nothing to do!
|
||||
}
|
||||
else Append(aDest,aSource,0,aCount);
|
||||
}
|
||||
else Append(aDest,aSource,0,aCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method deletes up to aCount chars from aDest
|
||||
* @update gess10/30/98
|
||||
* @param aDest is the nsStr to be manipulated
|
||||
* @param aDestOffset is where in aDest deletion is to occur
|
||||
* @param aCount is the number of chars to be deleted in aDest
|
||||
*/
|
||||
void nsStr::Delete(nsStr& aDest,PRUint32 aDestOffset,PRUint32 aCount){
|
||||
if(aDestOffset<aDest.mLength){
|
||||
|
||||
PRUint32 theDelta=aDest.mLength-aDestOffset;
|
||||
PRUint32 theLength=(theDelta<aCount) ? theDelta : aCount;
|
||||
|
||||
if(aDestOffset+theLength<aDest.mLength) {
|
||||
|
||||
//if you're here, it means we're cutting chars out of the middle of the string...
|
||||
//so shift the chars left by theLength...
|
||||
(*gShiftChars[aDest.mCharSize][KSHIFTLEFT])(aDest.mStr,aDest.mLength,aDestOffset,theLength);
|
||||
aDest.mLength-=theLength;
|
||||
AddNullTerminator(aDest);
|
||||
}
|
||||
else Truncate(aDest,aDestOffset);
|
||||
}//if
|
||||
}
|
||||
|
||||
/**
|
||||
* This method truncates the given nsStr at given offset
|
||||
* @update gess10/30/98
|
||||
* @param aDest is the nsStr to be truncated
|
||||
* @param aDestOffset is where in aDest truncation is to occur
|
||||
*/
|
||||
void nsStr::Truncate(nsStr& aDest,PRUint32 aDestOffset){
|
||||
if(aDestOffset<aDest.mLength){
|
||||
aDest.mLength=aDestOffset;
|
||||
AddNullTerminator(aDest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method forces the given string to upper or lowercase
|
||||
* @update gess1/7/99
|
||||
* @param aDest is the string you're going to change
|
||||
* @param aToUpper: if TRUE, then we go uppercase, otherwise we go lowercase
|
||||
* @return
|
||||
*/
|
||||
void nsStr::ChangeCase(nsStr& aDest,PRBool aToUpper) {
|
||||
// somehow UnicharUtil return failed, fallback to the old ascii only code
|
||||
gCaseConverters[aDest.mCharSize](aDest.mStr,aDest.mLength,aToUpper);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess1/7/99
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::Trim(nsStr& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing){
|
||||
|
||||
if((aDest.mLength>0) && aSet){
|
||||
PRInt32 theIndex=-1;
|
||||
PRInt32 theMax=aDest.mLength;
|
||||
PRInt32 theSetLen=nsCRT::strlen(aSet);
|
||||
|
||||
if(aEliminateLeading) {
|
||||
while(++theIndex<=theMax) {
|
||||
PRUnichar theChar=GetCharAt(aDest,theIndex);
|
||||
PRInt32 thePos=gFindChars[eOneByte](aSet,theSetLen,0,theChar,PR_FALSE);
|
||||
if(kNotFound==thePos)
|
||||
break;
|
||||
}
|
||||
if(0<theIndex) {
|
||||
if(theIndex<theMax) {
|
||||
Delete(aDest,0,theIndex);
|
||||
}
|
||||
else Truncate(aDest,0);
|
||||
}
|
||||
}
|
||||
|
||||
if(aEliminateTrailing) {
|
||||
theIndex=aDest.mLength;
|
||||
PRInt32 theNewLen=theIndex;
|
||||
while(--theIndex>0) {
|
||||
PRUnichar theChar=GetCharAt(aDest,theIndex); //read at end now...
|
||||
PRInt32 thePos=gFindChars[eOneByte](aSet,theSetLen,0,theChar,PR_FALSE);
|
||||
if(kNotFound<thePos)
|
||||
theNewLen=theIndex;
|
||||
else break;
|
||||
}
|
||||
if(theNewLen<theMax) {
|
||||
Truncate(aDest,theNewLen);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess1/7/99
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::CompressSet(nsStr& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing){
|
||||
Trim(aDest,aSet,aEliminateLeading,aEliminateTrailing);
|
||||
PRUint32 aNewLen=gCompressChars[aDest.mCharSize](aDest.mStr,aDest.mLength,aSet);
|
||||
aDest.mLength=aNewLen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @update gess1/7/99
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
void nsStr::StripChars(nsStr& aDest,const char* aSet){
|
||||
if((0<aDest.mLength) && (aSet)) {
|
||||
PRUint32 aNewLen=gStripChars[aDest.mCharSize](aDest.mStr,aDest.mLength,aSet);
|
||||
aDest.mLength=aNewLen;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************
|
||||
Searching methods...
|
||||
**************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* This searches aDest for a given substring
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param aTarget is the substring you're trying to find.
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::FindSubstr(const nsStr& aDest,const nsStr& aTarget, PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
// NS_PRECONDITION(aTarget.mLength!=1,kCallFindChar);
|
||||
|
||||
PRInt32 result=kNotFound;
|
||||
|
||||
if((0<aDest.mLength) && (anOffset<(PRInt32)aDest.mLength)) {
|
||||
PRInt32 theMax=aDest.mLength-aTarget.mLength;
|
||||
PRInt32 index=(0<=anOffset) ? anOffset : 0;
|
||||
|
||||
if((aDest.mLength>=aTarget.mLength) && (aTarget.mLength>0) && (index>=0)){
|
||||
PRInt32 theTargetMax=aTarget.mLength;
|
||||
while(index<=theMax) {
|
||||
PRInt32 theSubIndex=-1;
|
||||
PRBool matches=PR_TRUE;
|
||||
while((++theSubIndex<theTargetMax) && (matches)){
|
||||
PRUnichar theChar=(aIgnoreCase) ? nsCRT::ToLower(GetCharAt(aDest,index+theSubIndex)) : GetCharAt(aDest,index+theSubIndex);
|
||||
PRUnichar theTargetChar=(aIgnoreCase) ? nsCRT::ToLower(GetCharAt(aTarget,theSubIndex)) : GetCharAt(aTarget,theSubIndex);
|
||||
matches=PRBool(theChar==theTargetChar);
|
||||
}
|
||||
if(matches) {
|
||||
result=index;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
} //while
|
||||
}//if
|
||||
}//if
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This searches aDest for a given character
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param char is the character you're trying to find.
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::FindChar(const nsStr& aDest,PRUnichar aChar, PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
PRInt32 result=kNotFound;
|
||||
if((0<aDest.mLength) && (anOffset<(PRInt32)aDest.mLength)) {
|
||||
PRUint32 index=(0<=anOffset) ? (PRUint32)anOffset : 0;
|
||||
result=gFindChars[aDest.mCharSize](aDest.mStr,aDest.mLength,index,aChar,aIgnoreCase);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This searches aDest for a character found in aSet.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param aSet contains a list of chars to be searched for
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::FindCharInSet(const nsStr& aDest,const nsStr& aSet,PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
//NS_PRECONDITION(aSet.mLength!=1,kCallFindChar);
|
||||
|
||||
PRInt32 index=(0<=anOffset) ? anOffset-1 : -1;
|
||||
PRInt32 thePos;
|
||||
|
||||
//Note that the search is inverted here. We're scanning aDest, one char at a time
|
||||
//but doing the search against the given set. That's why we use 0 as the offset below.
|
||||
if((0<aDest.mLength) && (0<aSet.mLength)){
|
||||
while(++index<(PRInt32)aDest.mLength) {
|
||||
PRUnichar theChar=GetCharAt(aDest,index);
|
||||
thePos=gFindChars[aSet.mCharSize](aSet.mStr,aSet.mLength,0,theChar,aIgnoreCase);
|
||||
if(kNotFound!=thePos)
|
||||
return index;
|
||||
} //while
|
||||
}
|
||||
return kNotFound;
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
Reverse Searching methods...
|
||||
**************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* This searches aDest (in reverse) for a given substring
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param aTarget is the substring you're trying to find.
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search (counting from left)
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::RFindSubstr(const nsStr& aDest,const nsStr& aTarget, PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
//NS_PRECONDITION(aTarget.mLength!=1,kCallRFindChar);
|
||||
|
||||
PRInt32 result=kNotFound;
|
||||
|
||||
if((0<aDest.mLength) && (anOffset<(PRInt32)aDest.mLength)) {
|
||||
PRInt32 index=(0<=anOffset) ? anOffset : aDest.mLength-1;
|
||||
|
||||
if((aDest.mLength>=aTarget.mLength) && (aTarget.mLength>0) && (index>=0)){
|
||||
|
||||
nsStr theCopy;
|
||||
nsStr::Initialize(theCopy,eOneByte);
|
||||
nsStr::Assign(theCopy,aTarget,0,aTarget.mLength);
|
||||
if(aIgnoreCase){
|
||||
nsStr::ChangeCase(theCopy,PR_FALSE); //force to lowercase
|
||||
}
|
||||
|
||||
PRInt32 theTargetMax=theCopy.mLength;
|
||||
while(index>=0) {
|
||||
PRInt32 theSubIndex=-1;
|
||||
PRBool matches=PR_FALSE;
|
||||
if(index+theCopy.mLength<=aDest.mLength) {
|
||||
matches=PR_TRUE;
|
||||
while((++theSubIndex<theTargetMax) && (matches)){
|
||||
PRUnichar theDestChar=(aIgnoreCase) ? nsCRT::ToLower(GetCharAt(aDest,index+theSubIndex)) : GetCharAt(aDest,index+theSubIndex);
|
||||
PRUnichar theTargetChar=GetCharAt(theCopy,theSubIndex);
|
||||
matches=PRBool(theDestChar==theTargetChar);
|
||||
} //while
|
||||
} //if
|
||||
if(matches) {
|
||||
result=index;
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
} //while
|
||||
nsStr::Destroy(theCopy);
|
||||
}//if
|
||||
}//if
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This searches aDest (in reverse) for a given character
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param char is the character you're trying to find.
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::RFindChar(const nsStr& aDest,PRUnichar aChar, PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
PRInt32 result=kNotFound;
|
||||
if((0<aDest.mLength) && (anOffset<(PRInt32)aDest.mLength)) {
|
||||
PRUint32 index=(0<=anOffset) ? anOffset : aDest.mLength-1;
|
||||
result=gRFindChars[aDest.mCharSize](aDest.mStr,aDest.mLength,index,aChar,aIgnoreCase);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This searches aDest (in reverese) for a character found in aSet.
|
||||
*
|
||||
* @update gess 3/25/98
|
||||
* @param aDest string to search
|
||||
* @param aSet contains a list of chars to be searched for
|
||||
* @param aIgnorecase indicates case sensitivity of search
|
||||
* @param anOffset tells us where to start the search
|
||||
* @return index in aDest where member of aSet occurs, or -1 if not found
|
||||
*/
|
||||
PRInt32 nsStr::RFindCharInSet(const nsStr& aDest,const nsStr& aSet,PRBool aIgnoreCase,PRInt32 anOffset) {
|
||||
//NS_PRECONDITION(aSet.mLength!=1,kCallRFindChar);
|
||||
|
||||
PRInt32 index=(0<=anOffset) ? anOffset : aDest.mLength;
|
||||
PRInt32 thePos;
|
||||
|
||||
//note that the search is inverted here. We're scanning aDest, one char at a time
|
||||
//but doing the search against the given set. That's why we use 0 as the offset below.
|
||||
if(0<aDest.mLength) {
|
||||
while(--index>=0) {
|
||||
PRUnichar theChar=GetCharAt(aDest,index);
|
||||
thePos=gFindChars[aSet.mCharSize](aSet.mStr,aSet.mLength,0,theChar,aIgnoreCase);
|
||||
if(kNotFound!=thePos)
|
||||
return index;
|
||||
} //while
|
||||
}
|
||||
return kNotFound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compare source and dest strings, up to an (optional max) number of chars
|
||||
* @param aDest is the first str to compare
|
||||
* @param aSource is the second str to compare
|
||||
* @param aCount -- if (-1), then we use length of longer string; if (0<aCount) then it gives the max # of chars to compare
|
||||
* @param aIgnorecase tells us whether to search with case sensitivity
|
||||
* @return aDest<aSource=-1;aDest==aSource==0;aDest>aSource=1
|
||||
*/
|
||||
PRInt32 nsStr::Compare(const nsStr& aDest,const nsStr& aSource,PRInt32 aCount,PRBool aIgnoreCase) {
|
||||
PRInt32 result=0;
|
||||
|
||||
if(aCount) {
|
||||
PRInt32 minlen=(aSource.mLength<aDest.mLength) ? aSource.mLength : aDest.mLength;
|
||||
|
||||
if(0==minlen) {
|
||||
if ((aDest.mLength == 0) && (aSource.mLength == 0))
|
||||
return 0;
|
||||
if (aDest.mLength == 0)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
PRInt32 maxlen=(aSource.mLength<aDest.mLength) ? aDest.mLength : aSource.mLength;
|
||||
aCount = (aCount<0) ? maxlen : MinInt(aCount,maxlen);
|
||||
result=(*gCompare[aDest.mCharSize][aSource.mCharSize])(aDest.mStr,aSource.mStr,aCount,aIgnoreCase);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
PRBool nsStr::Alloc(nsStr& aDest,PRUint32 aCount) {
|
||||
|
||||
static int mAllocCount=0;
|
||||
mAllocCount++;
|
||||
|
||||
//we're given the acount value in charunits; now scale up to next multiple.
|
||||
PRUint32 theNewCapacity=kDefaultStringSize;
|
||||
while(theNewCapacity<aCount){
|
||||
theNewCapacity<<=1;
|
||||
}
|
||||
|
||||
aDest.mCapacity=theNewCapacity++;
|
||||
PRUint32 theSize=(theNewCapacity<<aDest.mCharSize);
|
||||
aDest.mStr = (char*)nsAllocator::Alloc(theSize);
|
||||
|
||||
PRBool result=PR_FALSE;
|
||||
if(aDest.mStr) {
|
||||
aDest.mOwnsBuffer=1;
|
||||
result=PR_TRUE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PRBool nsStr::Free(nsStr& aDest){
|
||||
if(aDest.mStr){
|
||||
if(aDest.mOwnsBuffer){
|
||||
nsAllocator::Free(aDest.mStr);
|
||||
}
|
||||
aDest.mStr=0;
|
||||
aDest.mOwnsBuffer=0;
|
||||
return PR_TRUE;
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool nsStr::Realloc(nsStr& aDest,PRUint32 aCount){
|
||||
|
||||
nsStr temp;
|
||||
memcpy(&temp,&aDest,sizeof(aDest));
|
||||
|
||||
PRBool result=Alloc(temp,aCount);
|
||||
if(result) {
|
||||
Free(aDest);
|
||||
aDest.mStr=temp.mStr;
|
||||
aDest.mCapacity=temp.mCapacity;
|
||||
aDest.mOwnsBuffer=temp.mOwnsBuffer;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
CBufDescriptor::CBufDescriptor(char* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) {
|
||||
mBuffer=aString;
|
||||
mCharSize=eOneByte;
|
||||
mStackBased=aStackBased;
|
||||
mIsConst=PR_FALSE;
|
||||
mLength=mCapacity=0;
|
||||
if(aString && aCapacity>1) {
|
||||
mCapacity=aCapacity-1;
|
||||
mLength=(-1==aLength) ? strlen(aString) : aLength;
|
||||
if(mLength>PRInt32(mCapacity))
|
||||
mLength=mCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
CBufDescriptor::CBufDescriptor(const char* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) {
|
||||
mBuffer=(char*)aString;
|
||||
mCharSize=eOneByte;
|
||||
mStackBased=aStackBased;
|
||||
mIsConst=PR_TRUE;
|
||||
mLength=mCapacity=0;
|
||||
if(aString && aCapacity>1) {
|
||||
mCapacity=aCapacity-1;
|
||||
mLength=(-1==aLength) ? strlen(aString) : aLength;
|
||||
if(mLength>PRInt32(mCapacity))
|
||||
mLength=mCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CBufDescriptor::CBufDescriptor(PRUnichar* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) {
|
||||
mBuffer=(char*)aString;
|
||||
mCharSize=eTwoByte;
|
||||
mStackBased=aStackBased;
|
||||
mLength=mCapacity=0;
|
||||
mIsConst=PR_FALSE;
|
||||
if(aString && aCapacity>1) {
|
||||
mCapacity=aCapacity-1;
|
||||
mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength;
|
||||
if(mLength>PRInt32(mCapacity))
|
||||
mLength=mCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
CBufDescriptor::CBufDescriptor(const PRUnichar* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength) {
|
||||
mBuffer=(char*)aString;
|
||||
mCharSize=eTwoByte;
|
||||
mStackBased=aStackBased;
|
||||
mLength=mCapacity=0;
|
||||
mIsConst=PR_TRUE;
|
||||
if(aString && aCapacity>1) {
|
||||
mCapacity=aCapacity-1;
|
||||
mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength;
|
||||
if(mLength>PRInt32(mCapacity))
|
||||
mLength=mCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
450
mozilla/string/obsolete/nsStr.h
Normal file
@@ -0,0 +1,450 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
MODULE NOTES:
|
||||
|
||||
1. There are two philosophies to building string classes:
|
||||
A. Hide the underlying buffer & offer API's allow indirect iteration
|
||||
B. Reveal underlying buffer, risk corruption, but gain performance
|
||||
|
||||
We chose the option B for performance reasons.
|
||||
|
||||
2 Our internal buffer always holds capacity+1 bytes.
|
||||
|
||||
The nsStr struct is a simple structure (no methods) that contains
|
||||
the necessary info to be described as a string. This simple struct
|
||||
is manipulated by the static methods provided in this class.
|
||||
(Which effectively makes this a library that works on structs).
|
||||
|
||||
There are also object-based versions called nsString and nsAutoString
|
||||
which use nsStr but makes it look at feel like an object.
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
/***********************************************************************
|
||||
ASSUMPTIONS:
|
||||
|
||||
1. nsStrings and nsAutoString are always null terminated.
|
||||
2. If you try to set a null char (via SetChar()) a new length is set
|
||||
3. nsCStrings can be upsampled into nsString without data loss
|
||||
4. Char searching is faster than string searching. Use char interfaces
|
||||
if your needs will allow it.
|
||||
5. It's easy to use the stack for nsAutostring buffer storage (fast too!).
|
||||
See the CBufDescriptor class in this file.
|
||||
6. It's ONLY ok to provide non-null-terminated buffers to Append() and Insert()
|
||||
provided you specify a 0<n value for the optional count argument.
|
||||
7. Downsampling from nsString to nsCString is lossy -- avoid it if possible!
|
||||
8. Calls to ToNewCString() and ToNewUnicode() should be matched with calls to Recycle().
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
|
||||
AND NOW FOR SOME GENERAL DOCUMENTATION ON STRING USAGE...
|
||||
|
||||
The fundamental datatype in the string library is nsStr. It's a structure that
|
||||
provides the buffer storage and meta-info. It also provides a C-style library
|
||||
of functions for direct manipulation (for those of you who prefer K&R to Bjarne).
|
||||
|
||||
Here's a diagram of the class hierarchy:
|
||||
|
||||
nsStr
|
||||
|___nsString
|
||||
| |
|
||||
| ------nsAutoString
|
||||
|
|
||||
|___nsCString
|
||||
|
|
||||
------nsCAutoString
|
||||
|
||||
Why so many string classes? The 4 variants give you the control you need to
|
||||
determine the best class for your purpose. There are 2 dimensions to this
|
||||
flexibility: 1) stack vs. heap; and 2) 1-byte chars vs. 2-byte chars.
|
||||
|
||||
Note: While nsAutoString and nsCAutoString begin life using stack-based storage,
|
||||
they may not stay that way. Like all nsString classes, autostrings will
|
||||
automatically grow to contain the data you provide. When autostrings
|
||||
grow beyond their intrinsic buffer, they switch to heap based allocations.
|
||||
(We avoid alloca to avoid considerable platform difficulties; see the
|
||||
GNU documentation for more details).
|
||||
|
||||
I should also briefly mention that all the string classes use a "memory agent"
|
||||
object to perform memory operations. This class proxies the standard nsAllocator
|
||||
for actual memory calls, but knows the structure of nsStr making heap operations
|
||||
more localized.
|
||||
|
||||
|
||||
CHOOSING A STRING CLASS:
|
||||
|
||||
In order to choose a string class for you purpose, use this handy table:
|
||||
|
||||
heap-based stack-based
|
||||
-----------------------------------
|
||||
ascii data | nsCString nsCAutoString |
|
||||
|----------------------------------
|
||||
unicode data | nsString nsAutoString |
|
||||
-----------------------------------
|
||||
|
||||
|
||||
Note: The i18n folks will stenuously object if we get too carried away with the
|
||||
use of nsCString's that pass interface boundaries. Try to limit your
|
||||
use of these to external interfaces that demand them, or for your own
|
||||
private purposes in cases where they'll never be seen by humans.
|
||||
|
||||
|
||||
PERFORMANCE CONSIDERATIONS:
|
||||
|
||||
Here are a few tricks to know in order to get better string performance:
|
||||
|
||||
1) Try to limit conversions between ascii and unicode; By sticking with nsString
|
||||
wherever possible your code will be i18n-compliant.
|
||||
|
||||
|
||||
2) Preallocating your string buffer cuts down trips to the allocator. So if you
|
||||
have need for an arbitrarily large buffer, pre-size it like this:
|
||||
|
||||
{
|
||||
nsString mBuffer;
|
||||
mBuffer.SetCapacity(aReasonableSize);
|
||||
}
|
||||
|
||||
3) Allocating nsAutoString or nsCAutoString on the heap is memory inefficient
|
||||
(after all, the whole point is to avoid a heap allocation of the buffer).
|
||||
|
||||
|
||||
4) Consider using an autoString to write into your arbitrarily-sized stack buffers, rather
|
||||
than it's own buffers.
|
||||
|
||||
For example, let's say you're going to call printf() to emit pretty-printed debug output
|
||||
of your object. You know from experience that the pretty-printed version of your object
|
||||
exceeds the capacity of an autostring. Ignoring memory considerations, you could simply
|
||||
use nsCString, appending the stringized version of each of your class's data members.
|
||||
This will probably result in calls to the heap manager.
|
||||
|
||||
But there's a way to do this without necessarily having to call the heap manager.
|
||||
All you do is declare a stack based buffer and instruct nsCString to use that instead
|
||||
of it's own internal buffer by using the CBufDescriptor class:
|
||||
|
||||
{
|
||||
char theBuffer[256];
|
||||
CBufDescritor theBufDecriptor( theBuffer, PR_TRUE, sizeof(theBuffer), 0);
|
||||
nsCAutoString s3( theBufDescriptor );
|
||||
s3="HELLO, my name is inigo montoya, you killed my father, prepare to die!.";
|
||||
}
|
||||
|
||||
The assignment statment to s3 will cause the given string to be written to your
|
||||
stack-based buffer via the normal nsString/nsCString interfaces. Cool, huh?
|
||||
Note however that just like any other nsStringXXX use, if you write more data
|
||||
than will fit in the buffer, a visit to the heap manager will be in order.
|
||||
|
||||
|
||||
**********************************************************************************/
|
||||
|
||||
|
||||
#ifndef _nsStr
|
||||
#define _nsStr
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsIAllocator.h"
|
||||
#include <string.h>
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
enum eCharSize {eOneByte=0,eTwoByte=1};
|
||||
#define kDefaultCharSize eTwoByte
|
||||
#define kRadix10 (10)
|
||||
#define kRadix16 (16)
|
||||
#define kAutoDetect (100)
|
||||
#define kRadixUnknown (kAutoDetect+1)
|
||||
const PRInt32 kDefaultStringSize = 64;
|
||||
const PRInt32 kNotFound = -1;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
class NS_COM CBufDescriptor {
|
||||
public:
|
||||
CBufDescriptor(char* aString, PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength=-1);
|
||||
CBufDescriptor(const char* aString, PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength=-1);
|
||||
CBufDescriptor(PRUnichar* aString, PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength=-1);
|
||||
CBufDescriptor(const PRUnichar* aString,PRBool aStackBased,PRUint32 aCapacity,PRInt32 aLength=-1);
|
||||
|
||||
char* mBuffer;
|
||||
eCharSize mCharSize;
|
||||
PRUint32 mCapacity;
|
||||
PRInt32 mLength;
|
||||
PRBool mStackBased;
|
||||
PRBool mIsConst;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
struct NS_COM nsStr {
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
nsStr() {
|
||||
MOZ_COUNT_CTOR(nsStr);
|
||||
}
|
||||
|
||||
~nsStr() {
|
||||
MOZ_COUNT_DTOR(nsStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method initializes an nsStr for use
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aString is the nsStr to be initialized
|
||||
* @param aCharSize tells us the requested char size (1 or 2 bytes)
|
||||
*/
|
||||
static void Initialize(nsStr& aDest,eCharSize aCharSize);
|
||||
|
||||
/**
|
||||
* This method initializes an nsStr for use
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aString is the nsStr to be initialized
|
||||
* @param aCharSize tells us the requested char size (1 or 2 bytes)
|
||||
*/
|
||||
static void Initialize(nsStr& aDest,char* aCString,PRUint32 aCapacity,PRUint32 aLength,eCharSize aCharSize,PRBool aOwnsBuffer);
|
||||
|
||||
/**
|
||||
* This method destroys the given nsStr, and *MAY*
|
||||
* deallocate it's memory depending on the setting
|
||||
* of the internal mOwnsBUffer flag.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aString is the nsStr to be manipulated
|
||||
* @param anAgent is the allocator to be used to the nsStr
|
||||
*/
|
||||
static void Destroy(nsStr& aDest);
|
||||
|
||||
/**
|
||||
* These methods are where memory allocation/reallocation occur.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aString is the nsStr to be manipulated
|
||||
* @param anAgent is the allocator to be used on the nsStr
|
||||
* @return
|
||||
*/
|
||||
static PRBool EnsureCapacity(nsStr& aString,PRUint32 aNewLength);
|
||||
static PRBool GrowCapacity(nsStr& aString,PRUint32 aNewLength);
|
||||
|
||||
/**
|
||||
* These methods are used to append content to the given nsStr
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be appended to
|
||||
* @param aSource is the buffer to be copied from
|
||||
* @param anOffset tells us where in source to start copying
|
||||
* @param aCount tells us the (max) # of chars to copy
|
||||
* @param anAgent is the allocator to be used for alloc/free operations
|
||||
*/
|
||||
static void Append(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount);
|
||||
|
||||
/**
|
||||
* These methods are used to assign contents of a source string to dest string
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be appended to
|
||||
* @param aSource is the buffer to be copied from
|
||||
* @param anOffset tells us where in source to start copying
|
||||
* @param aCount tells us the (max) # of chars to copy
|
||||
* @param anAgent is the allocator to be used for alloc/free operations
|
||||
*/
|
||||
static void Assign(nsStr& aDest,const nsStr& aSource,PRUint32 anOffset,PRInt32 aCount);
|
||||
|
||||
/**
|
||||
* These methods are used to insert content from source string to the dest nsStr
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be appended to
|
||||
* @param aDestOffset tells us where in dest to start insertion
|
||||
* @param aSource is the buffer to be copied from
|
||||
* @param aSrcOffset tells us where in source to start copying
|
||||
* @param aCount tells us the (max) # of chars to insert
|
||||
* @param anAgent is the allocator to be used for alloc/free operations
|
||||
*/
|
||||
static void Insert( nsStr& aDest,PRUint32 aDestOffset,const nsStr& aSource,PRUint32 aSrcOffset,PRInt32 aCount);
|
||||
|
||||
/**
|
||||
* This method deletes chars from the given str.
|
||||
* The given allocator may choose to resize the str as well.
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be deleted from
|
||||
* @param aDestOffset tells us where in dest to start deleting
|
||||
* @param aCount tells us the (max) # of chars to delete
|
||||
* @param anAgent is the allocator to be used for alloc/free operations
|
||||
*/
|
||||
static void Delete(nsStr& aDest,PRUint32 aDestOffset,PRUint32 aCount);
|
||||
|
||||
/**
|
||||
* This method is used to truncate the given string.
|
||||
* The given allocator may choose to resize the str as well (but it's not likely).
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be appended to
|
||||
* @param aDestOffset tells us where in dest to start insertion
|
||||
* @param aSource is the buffer to be copied from
|
||||
* @param aSrcOffset tells us where in source to start copying
|
||||
* @param anAgent is the allocator to be used for alloc/free operations
|
||||
*/
|
||||
static void Truncate(nsStr& aDest,PRUint32 aDestOffset);
|
||||
|
||||
/**
|
||||
* This method is used to perform a case conversion on the given string
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be case shifted
|
||||
* @param toUpper tells us to go upper vs. lower
|
||||
*/
|
||||
static void ChangeCase(nsStr& aDest,PRBool aToUpper);
|
||||
|
||||
|
||||
/**
|
||||
* This method trims chars (given in aSet) from the edges of given buffer
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the buffer to be manipulated
|
||||
* @param aSet tells us which chars to remove from given buffer
|
||||
* @param aEliminateLeading tells us whether to strip chars from the start of the buffer
|
||||
* @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
|
||||
*/
|
||||
static void Trim(nsStr& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing);
|
||||
|
||||
/**
|
||||
* This method compresses duplicate runs of a given char from the given buffer
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the buffer to be manipulated
|
||||
* @param aSet tells us which chars to compress from given buffer
|
||||
* @param aChar is the replacement char
|
||||
* @param aEliminateLeading tells us whether to strip chars from the start of the buffer
|
||||
* @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
|
||||
*/
|
||||
static void CompressSet(nsStr& aDest,const char* aSet,PRBool aEliminateLeading,PRBool aEliminateTrailing);
|
||||
|
||||
/**
|
||||
* This method removes all occurances of chars in given set from aDest
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the buffer to be manipulated
|
||||
* @param aSet tells us which chars to compress from given buffer
|
||||
* @param aChar is the replacement char
|
||||
* @param aEliminateLeading tells us whether to strip chars from the start of the buffer
|
||||
* @param aEliminateTrailing tells us whether to strip chars from the start of the buffer
|
||||
*/
|
||||
static void StripChars(nsStr& aDest,const char* aSet);
|
||||
|
||||
/**
|
||||
* This method compares the data bewteen two nsStr's
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aStr1 is the first buffer to be compared
|
||||
* @param aStr2 is the 2nd buffer to be compared
|
||||
* @param aCount is the number of chars to compare
|
||||
* @param aIgnorecase tells us whether to use a case-sensitive comparison
|
||||
* @return -1,0,1 depending on <,==,>
|
||||
*/
|
||||
static PRInt32 Compare(const nsStr& aDest,const nsStr& aSource,PRInt32 aCount,PRBool aIgnoreCase);
|
||||
|
||||
/**
|
||||
* These methods scan the given string for 1 or more chars in a given direction
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be searched to
|
||||
* @param aSource (or aChar) is the substr we're looking to find
|
||||
* @param aIgnoreCase tells us whether to search in a case-sensitive manner
|
||||
* @param anOffset tells us where in the dest string to start searching
|
||||
* @return the index of the source (substr) in dest, or -1 (kNotFound) if not found.
|
||||
*/
|
||||
static PRInt32 FindSubstr(const nsStr& aDest,const nsStr& aSource, PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
static PRInt32 FindChar(const nsStr& aDest,PRUnichar aChar, PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
static PRInt32 FindCharInSet(const nsStr& aDest,const nsStr& aSet,PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
|
||||
static PRInt32 RFindSubstr(const nsStr& aDest,const nsStr& aSource, PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
static PRInt32 RFindChar(const nsStr& aDest,PRUnichar aChar, PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
static PRInt32 RFindCharInSet(const nsStr& aDest,const nsStr& aSet,PRBool aIgnoreCase,PRInt32 anOffset);
|
||||
|
||||
|
||||
PRUint32 mLength;
|
||||
PRUint32 mCapacity;
|
||||
eCharSize mCharSize;
|
||||
PRBool mOwnsBuffer;
|
||||
|
||||
union {
|
||||
char* mStr;
|
||||
PRUnichar* mUStr;
|
||||
};
|
||||
|
||||
private:
|
||||
static PRBool Alloc(nsStr& aString,PRUint32 aCount);
|
||||
static PRBool Realloc(nsStr& aString,PRUint32 aCount);
|
||||
static PRBool Free(nsStr& aString);
|
||||
|
||||
};
|
||||
|
||||
/**************************************************************
|
||||
A couple of tiny helper methods used in the string classes.
|
||||
**************************************************************/
|
||||
|
||||
inline PRInt32 MinInt(PRInt32 anInt1,PRInt32 anInt2){
|
||||
return (anInt1<anInt2) ? anInt1 : anInt2;
|
||||
}
|
||||
|
||||
inline PRInt32 MaxInt(PRInt32 anInt1,PRInt32 anInt2){
|
||||
return (anInt1<anInt2) ? anInt2 : anInt1;
|
||||
}
|
||||
|
||||
inline void AddNullTerminator(nsStr& aDest) {
|
||||
if(eTwoByte==aDest.mCharSize)
|
||||
aDest.mUStr[aDest.mLength]=0;
|
||||
else aDest.mStr[aDest.mLength]=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given buffer to the heap manager. Calls allocator::Free()
|
||||
* @return string length
|
||||
*/
|
||||
inline void Recycle( char* aBuffer) { nsAllocator::Free(aBuffer); }
|
||||
inline void Recycle( PRUnichar* aBuffer) { nsAllocator::Free(aBuffer); }
|
||||
|
||||
/**
|
||||
* This method is used to access a given char in the given string
|
||||
*
|
||||
* @update gess 01/04/99
|
||||
* @param aDest is the nsStr to be appended to
|
||||
* @param anIndex tells us where in dest to get the char from
|
||||
* @return the given char, or 0 if anIndex is out of range
|
||||
*/
|
||||
inline PRUnichar GetCharAt(const nsStr& aDest,PRUint32 anIndex){
|
||||
if(anIndex<aDest.mLength) {
|
||||
return (eTwoByte==aDest.mCharSize) ? aDest.mUStr[anIndex] : aDest.mStr[anIndex];
|
||||
}//if
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1865
mozilla/string/obsolete/nsString.cpp
Normal file
747
mozilla/string/obsolete/nsString.h
Normal file
@@ -0,0 +1,747 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
MODULE NOTES:
|
||||
|
||||
See nsStr.h for a more general description of string classes.
|
||||
|
||||
This version of the nsString class offers many improvements over the
|
||||
original version:
|
||||
1. Wide and narrow chars
|
||||
2. Allocators
|
||||
3. Much smarter autostrings
|
||||
4. Subsumable strings
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
#ifndef _nsCString_
|
||||
#define _nsCString_
|
||||
|
||||
#include "nsString2.h"
|
||||
#include "prtypes.h"
|
||||
#include "nscore.h"
|
||||
#include <stdio.h>
|
||||
#include "nsStr.h"
|
||||
#include "nsIAtom.h"
|
||||
|
||||
|
||||
class NS_COM nsSubsumeCStr;
|
||||
|
||||
class NS_COM nsCString : public nsStr {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
nsCString();
|
||||
|
||||
/**
|
||||
* This constructor accepts an isolatin string
|
||||
* @param aCString is a ptr to a 1-byte cstr
|
||||
*/
|
||||
nsCString(const char* aCString,PRInt32 aLength=-1);
|
||||
|
||||
/**
|
||||
* This constructor accepts a unichar string
|
||||
* @param aCString is a ptr to a 2-byte cstr
|
||||
*/
|
||||
nsCString(const PRUnichar* aString,PRInt32 aLength=-1);
|
||||
|
||||
/**
|
||||
* This is a copy constructor that accepts an nsStr
|
||||
* @param reference to another nsCString
|
||||
*/
|
||||
nsCString(const nsStr&);
|
||||
|
||||
/**
|
||||
* This is our copy constructor
|
||||
* @param reference to another nsCString
|
||||
*/
|
||||
nsCString(const nsCString& aString);
|
||||
|
||||
/**
|
||||
* This constructor takes a subsumestr
|
||||
* @param reference to subsumestr
|
||||
*/
|
||||
nsCString(nsSubsumeCStr& aSubsumeStr);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
*/
|
||||
virtual ~nsCString();
|
||||
|
||||
/**
|
||||
* Retrieve the length of this string
|
||||
* @return string length
|
||||
*/
|
||||
inline PRInt32 Length() const { return (PRInt32)mLength; }
|
||||
|
||||
/**
|
||||
* Retrieve the size of this string
|
||||
* @return string length
|
||||
*/
|
||||
virtual void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
|
||||
|
||||
|
||||
/**
|
||||
* Call this method if you want to force a different string capacity
|
||||
* @update gess7/30/98
|
||||
* @param aLength -- contains new length for mStr
|
||||
* @return
|
||||
*/
|
||||
void SetLength(PRUint32 aLength) {
|
||||
Truncate(aLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new length of the string.
|
||||
* @param aLength is new string length.
|
||||
* @return nada
|
||||
*/
|
||||
void SetCapacity(PRUint32 aLength);
|
||||
/**
|
||||
* This method truncates this string to given length.
|
||||
*
|
||||
* @param anIndex -- new length of string
|
||||
* @return nada
|
||||
*/
|
||||
void Truncate(PRInt32 anIndex=0);
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether or not the characters in this
|
||||
* string are in sorted order.
|
||||
*
|
||||
* @return TRUE if ordered.
|
||||
*/
|
||||
PRBool IsOrdered(void) const;
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether or not this string has a length of 0
|
||||
*
|
||||
* @return TRUE if empty.
|
||||
*/
|
||||
PRBool IsEmpty(void) const {
|
||||
return PRBool(0==mLength);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Accessor methods...
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve const ptr to internal buffer; DO NOT TRY TO FREE IT!
|
||||
*/
|
||||
const char* GetBuffer(void) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get nth character.
|
||||
*/
|
||||
PRUnichar operator[](PRUint32 anIndex) const;
|
||||
PRUnichar CharAt(PRUint32 anIndex) const;
|
||||
PRUnichar First(void) const;
|
||||
PRUnichar Last(void) const;
|
||||
|
||||
PRBool SetCharAt(PRUnichar aChar,PRUint32 anIndex);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
String creation methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Create a new string by appending given string to this
|
||||
* @param aString -- 2nd string to be appended
|
||||
* @return new string
|
||||
*/
|
||||
nsSubsumeCStr operator+(const nsCString& aString);
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given char*.
|
||||
* @param aCString is a ptr to cstring to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeCStr operator+(const char* aCString);
|
||||
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given char.
|
||||
* @param aChar is a char to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeCStr operator+(PRUnichar aChar);
|
||||
nsSubsumeCStr operator+(char aChar);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
Lexomorphic transforms...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase
|
||||
* @update gess 7/27/98
|
||||
*/
|
||||
void ToLowerCase();
|
||||
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase, and
|
||||
* stores them in aOut
|
||||
* @update gess 7/27/98
|
||||
* @param aOut is a string to contain result
|
||||
*/
|
||||
void ToLowerCase(nsCString& aString) const;
|
||||
|
||||
/**
|
||||
* Converts chars in this to uppercase
|
||||
* @update gess 7/27/98
|
||||
*/
|
||||
void ToUpperCase();
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase, and
|
||||
* stores them in a given output string
|
||||
* @update gess 7/27/98
|
||||
* @param aOut is a string to contain result
|
||||
*/
|
||||
void ToUpperCase(nsCString& aString) const;
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to remove all occurances of the
|
||||
* characters found in aSet from this string.
|
||||
*
|
||||
* @param aSet -- characters to be cut from this
|
||||
* @return *this
|
||||
*/
|
||||
nsCString& StripChars(const char* aSet);
|
||||
nsCString& StripChar(char aChar);
|
||||
|
||||
/**
|
||||
* This method strips whitespace throughout the string
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
nsCString& StripWhitespace();
|
||||
|
||||
/**
|
||||
* swaps occurence of 1 string for another
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
nsCString& ReplaceChar(PRUnichar aOldChar,PRUnichar aNewChar);
|
||||
nsCString& ReplaceChar(const char* aSet,PRUnichar aNewChar);
|
||||
|
||||
PRInt32 CountChar(PRUnichar aChar);
|
||||
|
||||
/**
|
||||
* This method trims characters found in aTrimSet from
|
||||
* either end of the underlying string.
|
||||
*
|
||||
* @param aTrimSet -- contains chars to be trimmed from
|
||||
* both ends
|
||||
* @return this
|
||||
*/
|
||||
nsCString& Trim(const char* aSet,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**
|
||||
* This method strips whitespace from string.
|
||||
* You can control whether whitespace is yanked from
|
||||
* start and end of string as well.
|
||||
*
|
||||
* @param aEliminateLeading controls stripping of leading ws
|
||||
* @param aEliminateTrailing controls stripping of trailing ws
|
||||
* @return this
|
||||
*/
|
||||
nsCString& CompressSet(const char* aSet, PRUnichar aChar,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**
|
||||
* This method strips whitespace from string.
|
||||
* You can control whether whitespace is yanked from
|
||||
* start and end of string as well.
|
||||
*
|
||||
* @param aEliminateLeading controls stripping of leading ws
|
||||
* @param aEliminateTrailing controls stripping of trailing ws
|
||||
* @return this
|
||||
*/
|
||||
nsCString& CompressWhitespace( PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**********************************************************************
|
||||
string conversion methods...
|
||||
*********************************************************************/
|
||||
|
||||
operator char*() {return mStr;}
|
||||
operator const char*() const {return (const char*)mStr;}
|
||||
|
||||
/**
|
||||
* This method constructs a new nsCString that is a clone
|
||||
* of this string.
|
||||
*
|
||||
*/
|
||||
nsCString* ToNewString() const;
|
||||
|
||||
/**
|
||||
* Creates an ISOLatin1 clone of this string
|
||||
* Note that calls to this method should be matched with calls to Recycle().
|
||||
* @return ptr to new isolatin1 string
|
||||
*/
|
||||
char* ToNewCString() const;
|
||||
|
||||
/**
|
||||
* Creates a unicode clone of this string
|
||||
* Note that calls to this method should be matched with calls to Recycle().
|
||||
* @return ptr to new unicode string
|
||||
*/
|
||||
PRUnichar* ToNewUnicode() const;
|
||||
|
||||
/**
|
||||
* Copies data from internal buffer onto given char* buffer
|
||||
* NOTE: This only copies as many chars as will fit in given buffer (clips)
|
||||
* @param aBuf is the buffer where data is stored
|
||||
* @param aBuflength is the max # of chars to move to buffer
|
||||
* @return ptr to given buffer
|
||||
*/
|
||||
char* ToCString(char* aBuf,PRUint32 aBufLength,PRUint32 anOffset=0) const;
|
||||
|
||||
/**
|
||||
* Perform string to float conversion.
|
||||
* @param aErrorCode will contain error if one occurs
|
||||
* @return float rep of string value
|
||||
*/
|
||||
float ToFloat(PRInt32* aErrorCode) const;
|
||||
|
||||
/**
|
||||
* Try to derive the radix from the value contained in this string
|
||||
* @return kRadix10, kRadix16 or kAutoDetect (meaning unknown)
|
||||
*/
|
||||
PRUint32 DetermineRadix(void);
|
||||
|
||||
/**
|
||||
* Perform string to int conversion.
|
||||
* @param aErrorCode will contain error if one occurs
|
||||
* @return int rep of string value
|
||||
*/
|
||||
PRInt32 ToInteger(PRInt32* aErrorCode,PRUint32 aRadix=kRadix10) const;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
String manipulation methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Functionally equivalent to assign or operator=
|
||||
*
|
||||
*/
|
||||
nsCString& SetString(const char* aString,PRInt32 aLength=-1) {return Assign(aString,aLength);}
|
||||
nsCString& SetString(const nsStr& aString,PRInt32 aLength=-1) {return Assign(aString,aLength);}
|
||||
|
||||
/**
|
||||
* assign given string to this string
|
||||
* @param aStr: buffer to be assigned to this
|
||||
* @param alength is the length of the given str (or -1)
|
||||
if you want me to determine its length
|
||||
* @return this
|
||||
*/
|
||||
nsCString& Assign(const nsStr& aString,PRInt32 aCount=-1);
|
||||
nsCString& Assign(const char* aString,PRInt32 aCount=-1);
|
||||
nsCString& Assign(const PRUnichar* aString,PRInt32 aCount=-1);
|
||||
nsCString& Assign(PRUnichar aChar);
|
||||
nsCString& Assign(char aChar);
|
||||
|
||||
/**
|
||||
* here come a bunch of assignment operators...
|
||||
* @param aString: string to be added to this
|
||||
* @return this
|
||||
*/
|
||||
nsCString& operator=(const nsCString& aString) {return Assign(aString);}
|
||||
nsCString& operator=(const nsStr& aString) {return Assign(aString);}
|
||||
nsCString& operator=(PRUnichar aChar) {return Assign(aChar);}
|
||||
nsCString& operator=(char aChar) {return Assign(aChar);}
|
||||
nsCString& operator=(const char* aCString) {return Assign(aCString);}
|
||||
nsCString& operator=(const PRUnichar* aString) {return Assign(aString);}
|
||||
#ifdef AIX
|
||||
nsCString& operator=(const nsSubsumeCStr& aSubsumeString); // AIX requires a const here
|
||||
#else
|
||||
nsCString& operator=(nsSubsumeCStr& aSubsumeString);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Here's a bunch of methods that append varying types...
|
||||
* @param various...
|
||||
* @return this
|
||||
*/
|
||||
nsCString& operator+=(const nsCString& aString){return Append(aString,aString.mLength);}
|
||||
nsCString& operator+=(const char* aCString) {return Append(aCString);}
|
||||
nsCString& operator+=(PRUnichar aChar){return Append(aChar);}
|
||||
nsCString& operator+=(char aChar){return Append(aChar);}
|
||||
|
||||
/*
|
||||
* Appends n characters from given string to this,
|
||||
* This version computes the length of your given string
|
||||
*
|
||||
* @param aString is the source to be appended to this
|
||||
* @return number of chars copied
|
||||
*/
|
||||
nsCString& Append(const nsCString& aString) {return Append(aString,aString.mLength);}
|
||||
|
||||
|
||||
/*
|
||||
* Appends n characters from given string to this,
|
||||
*
|
||||
* @param aString is the source to be appended to this
|
||||
* @param aCount -- number of chars to copy; -1 tells us to compute the strlen for you
|
||||
* @return number of chars copied
|
||||
*/
|
||||
nsCString& Append(const nsCString& aString,PRInt32 aCount);
|
||||
nsCString& Append(const nsStr& aString,PRInt32 aCount=-1);
|
||||
nsCString& Append(const char* aString,PRInt32 aCount=-1);
|
||||
nsCString& Append(PRUnichar aChar);
|
||||
nsCString& Append(char aChar);
|
||||
nsCString& Append(PRInt32 aInteger,PRInt32 aRadix=10); //radix=8,10 or 16
|
||||
nsCString& Append(float aFloat);
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at the leftmost offset.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Left(nsCString& aCopy,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at the given offset.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @param anOffset -- position where copying begins
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Mid(nsCString& aCopy,PRUint32 anOffset,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at rightmost char.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Right(nsCString& aCopy,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* This method inserts n chars from given string into this
|
||||
* string at str[anOffset].
|
||||
*
|
||||
* @param aCopy -- String to be inserted into this
|
||||
* @param anOffset -- insertion position within this str
|
||||
* @param aCount -- number of chars to be copied from aCopy
|
||||
* @return number of chars inserted into this.
|
||||
*/
|
||||
nsCString& Insert(const nsCString& aCopy,PRUint32 anOffset,PRInt32 aCount=-1);
|
||||
|
||||
/**
|
||||
* Insert a given string into this string at
|
||||
* a specified offset.
|
||||
*
|
||||
* @param aString* to be inserted into this string
|
||||
* @param anOffset is insert pos in str
|
||||
* @return the number of chars inserted into this string
|
||||
*/
|
||||
nsCString& Insert(const char* aChar,PRUint32 anOffset,PRInt32 aCount=-1);
|
||||
|
||||
/**
|
||||
* Insert a single char into this string at
|
||||
* a specified offset.
|
||||
*
|
||||
* @param character to be inserted into this string
|
||||
* @param anOffset is insert pos in str
|
||||
* @return the number of chars inserted into this string
|
||||
*/
|
||||
nsCString& Insert(PRUnichar aChar,PRUint32 anOffset);
|
||||
nsCString& Insert(char aChar,PRUint32 anOffset);
|
||||
|
||||
/*
|
||||
* This method is used to cut characters in this string
|
||||
* starting at anOffset, continuing for aCount chars.
|
||||
*
|
||||
* @param anOffset -- start pos for cut operation
|
||||
* @param aCount -- number of chars to be cut
|
||||
* @return *this
|
||||
*/
|
||||
nsCString& Cut(PRUint32 anOffset,PRInt32 aCount);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
Searching methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Search for given character within this string.
|
||||
* This method does so by using a binary search,
|
||||
* so your string HAD BETTER BE ORDERED!
|
||||
*
|
||||
* @param aChar is the unicode char to be found
|
||||
* @return offset in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 BinarySearch(PRUnichar aChar) const;
|
||||
|
||||
/**
|
||||
* Search for given substring within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @param anOffset tells us where in this strig to start searching
|
||||
* @return offset in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 Find(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 Find(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 Find(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
/**
|
||||
* Search for given char within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param anOffset tells us where in this strig to start searching
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @return find pos in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 FindChar(PRUnichar aChar,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
/**
|
||||
* This method searches this string for the first character
|
||||
* found in the given charset
|
||||
* @param aString contains set of chars to be found
|
||||
* @param anOffset tells us where to start searching in this
|
||||
* @return -1 if not found, else the offset in this
|
||||
*/
|
||||
PRInt32 FindCharInSet(const char* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 FindCharInSet(const PRUnichar* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 FindCharInSet(const nsStr& aString,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**
|
||||
* This methods scans the string backwards, looking for the given string
|
||||
* @param aString is substring to be sought in this
|
||||
* @param aIgnoreCase tells us whether or not to do caseless compare
|
||||
* @return offset in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 RFind(const char* aCString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFind(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFind(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**
|
||||
* Search for given char within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param anOffset tells us where in this strig to start searching
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @return find pos in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 RFindChar(PRUnichar aChar,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
/**
|
||||
* This method searches this string for the last character
|
||||
* found in the given string
|
||||
* @param aString contains set of chars to be found
|
||||
* @param anOffset tells us where to start searching in this
|
||||
* @return -1 if not found, else the offset in this
|
||||
*/
|
||||
PRInt32 RFindCharInSet(const char* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFindCharInSet(const PRUnichar* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFindCharInSet(const nsStr& aString,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
Comparison methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Compares a given string type to this string.
|
||||
* @update gess 7/27/98
|
||||
* @param S is the string to be compared
|
||||
* @param aIgnoreCase tells us how to treat case
|
||||
* @param aCount tells us how many chars to compare
|
||||
* @return -1,0,1
|
||||
*/
|
||||
virtual PRInt32 Compare(const nsStr &aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
virtual PRInt32 Compare(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
virtual PRInt32 Compare(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
|
||||
/**
|
||||
* These methods compare a given string type to this one
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator==(const nsStr &aString) const;
|
||||
PRBool operator==(const char* aString) const;
|
||||
PRBool operator==(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods perform a !compare of a given string type to this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE
|
||||
*/
|
||||
PRBool operator!=(const nsStr &aString) const;
|
||||
PRBool operator!=(const char* aString) const;
|
||||
PRBool operator!=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is < than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator<(const nsStr &aString) const;
|
||||
PRBool operator<(const char* aString) const;
|
||||
PRBool operator<(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is > than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator>(const nsStr &S) const;
|
||||
PRBool operator>(const char* aString) const;
|
||||
PRBool operator>(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is <= than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator<=(const nsStr &S) const;
|
||||
PRBool operator<=(const char* aString) const;
|
||||
PRBool operator<=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is >= than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator>=(const nsStr &S) const;
|
||||
PRBool operator>=(const char* aString) const;
|
||||
PRBool operator>=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* Compare this to given string; note that we compare full strings here.
|
||||
* The optional length argument just lets us know how long the given string is.
|
||||
* If you provide a length, it is compared to length of this string as an
|
||||
* optimization.
|
||||
*
|
||||
* @param aString -- the string to compare to this
|
||||
* @param aCount -- number of chars in given string you want to compare
|
||||
* @return TRUE if equal
|
||||
*/
|
||||
PRBool Equals(const nsString &aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
|
||||
PRBool EqualsIgnoreCase(const nsStr& aString) const;
|
||||
PRBool EqualsIgnoreCase(const char* aString,PRInt32 aCount=-1) const;
|
||||
PRBool EqualsIgnoreCase(const PRUnichar* aString,PRInt32 aCount=-1) const;
|
||||
|
||||
|
||||
static void Recycle(nsCString* aString);
|
||||
static nsCString* CreateString(void);
|
||||
|
||||
};
|
||||
|
||||
extern NS_COM int fputs(const nsCString& aString, FILE* out);
|
||||
//ostream& operator<<(ostream& aStream,const nsCString& aString);
|
||||
//virtual void DebugDump(ostream& aStream) const;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
Here comes the AutoString class which uses internal memory
|
||||
(typically found on the stack) for its default buffer.
|
||||
If the buffer needs to grow, it gets reallocated on the heap.
|
||||
**************************************************************/
|
||||
|
||||
class NS_COM nsCAutoString : public nsCString {
|
||||
public:
|
||||
|
||||
nsCAutoString();
|
||||
nsCAutoString(const char* aString,PRInt32 aLength=-1);
|
||||
nsCAutoString(const CBufDescriptor& aBuffer);
|
||||
nsCAutoString(const PRUnichar* aString,PRInt32 aLength=-1);
|
||||
nsCAutoString(const nsStr& aString);
|
||||
nsCAutoString(const nsCAutoString& aString);
|
||||
|
||||
#ifdef AIX
|
||||
nsCAutoString(const nsSubsumeCStr& aSubsumeStr); // AIX requires a const
|
||||
#else
|
||||
nsCAutoString(nsSubsumeCStr& aSubsumeStr);
|
||||
#endif // AIX
|
||||
nsCAutoString(PRUnichar aChar);
|
||||
virtual ~nsCAutoString();
|
||||
|
||||
nsCAutoString& operator=(const nsCString& aString) {nsCString::Assign(aString); return *this;}
|
||||
nsCAutoString& operator=(const char* aCString) {nsCString::Assign(aCString); return *this;}
|
||||
nsCAutoString& operator=(PRUnichar aChar) {nsCString::Assign(aChar); return *this;}
|
||||
nsCAutoString& operator=(char aChar) {nsCString::Assign(aChar); return *this;}
|
||||
|
||||
/**
|
||||
* Retrieve the size of this string
|
||||
* @return string length
|
||||
*/
|
||||
virtual void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
|
||||
|
||||
char mBuffer[kDefaultStringSize];
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************
|
||||
The subsumestr class is very unusual.
|
||||
It differs from a normal string in that it doesn't use normal
|
||||
copy semantics when another string is assign to this.
|
||||
Instead, it "steals" the contents of the source string.
|
||||
|
||||
This is very handy for returning nsString classes as part of
|
||||
an operator+(...) for example, in that it cuts down the number
|
||||
of copy operations that must occur.
|
||||
|
||||
You should probably not use this class unless you really know
|
||||
what you're doing.
|
||||
***************************************************************/
|
||||
class NS_COM nsSubsumeCStr : public nsCString {
|
||||
public:
|
||||
nsSubsumeCStr(nsStr& aString);
|
||||
nsSubsumeCStr(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength=-1);
|
||||
nsSubsumeCStr(char* aString,PRBool assumeOwnership,PRInt32 aLength=-1);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
2233
mozilla/string/obsolete/nsString2.cpp
Normal file
840
mozilla/string/obsolete/nsString2.h
Normal file
@@ -0,0 +1,840 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
MODULE NOTES:
|
||||
|
||||
See nsStr.h for a more general description of string classes.
|
||||
|
||||
This version of the nsString class offers many improvements over the
|
||||
original version:
|
||||
1. Wide and narrow chars
|
||||
2. Allocators
|
||||
3. Much smarter autostrings
|
||||
4. Subsumable strings
|
||||
***********************************************************************/
|
||||
|
||||
|
||||
#ifndef _nsString_
|
||||
#define _nsString_
|
||||
|
||||
#include "prtypes.h"
|
||||
#include "nscore.h"
|
||||
#include <stdio.h>
|
||||
#include "nsString.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsStr.h"
|
||||
#include "nsCRT.h"
|
||||
|
||||
class nsISizeOfHandler;
|
||||
|
||||
|
||||
#define nsString2 nsString
|
||||
#define nsAutoString2 nsAutoString
|
||||
|
||||
|
||||
class NS_COM nsSubsumeStr;
|
||||
class NS_COM nsString : public nsStr {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
nsString();
|
||||
|
||||
|
||||
/**
|
||||
* This constructor accepts an isolatin string
|
||||
* @param aCString is a ptr to a 1-byte cstr
|
||||
*/
|
||||
nsString(const char* aCString);
|
||||
|
||||
/**
|
||||
* This constructor accepts a unichar string
|
||||
* @param aCString is a ptr to a 2-byte cstr
|
||||
*/
|
||||
nsString(const PRUnichar* aString);
|
||||
|
||||
/**
|
||||
* This is a copy constructor that accepts an nsStr
|
||||
* @param reference to another nsString
|
||||
*/
|
||||
nsString(const nsStr&);
|
||||
|
||||
/**
|
||||
* This is our copy constructor
|
||||
* @param reference to another nsString
|
||||
*/
|
||||
nsString(const nsString& aString);
|
||||
|
||||
/**
|
||||
* This constructor takes a subsumestr
|
||||
* @param reference to subsumestr
|
||||
*/
|
||||
nsString(nsSubsumeStr& aSubsumeStr);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
*/
|
||||
virtual ~nsString();
|
||||
|
||||
/**
|
||||
* Retrieve the length of this string
|
||||
* @return string length
|
||||
*/
|
||||
inline PRInt32 Length() const { return (PRInt32)mLength; }
|
||||
|
||||
/**
|
||||
* Retrieve the size of this string
|
||||
* @return string length
|
||||
*/
|
||||
virtual void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
|
||||
|
||||
|
||||
/**
|
||||
* Call this method if you want to force a different string length
|
||||
* @update gess7/30/98
|
||||
* @param aLength -- contains new length for mStr
|
||||
* @return
|
||||
*/
|
||||
void SetLength(PRUint32 aLength) {
|
||||
Truncate(aLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new length of the string.
|
||||
* @param aLength is new string length.
|
||||
* @return nada
|
||||
*/
|
||||
void SetCapacity(PRUint32 aLength);
|
||||
|
||||
/**
|
||||
* This method truncates this string to given length.
|
||||
*
|
||||
* @param anIndex -- new length of string
|
||||
* @return nada
|
||||
*/
|
||||
void Truncate(PRInt32 anIndex=0);
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether or not the characters in this
|
||||
* string are in sorted order.
|
||||
*
|
||||
* @return TRUE if ordered.
|
||||
*/
|
||||
PRBool IsOrdered(void) const;
|
||||
|
||||
/**
|
||||
* Determine whether or not the characters in this
|
||||
* string are in store as 1 or 2 byte (unicode) strings.
|
||||
*
|
||||
* @return TRUE if ordered.
|
||||
*/
|
||||
PRBool IsUnicode(void) const {
|
||||
PRBool result=PRBool(mCharSize==eTwoByte);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not this string has a length of 0
|
||||
*
|
||||
* @return TRUE if empty.
|
||||
*/
|
||||
PRBool IsEmpty(void) const {
|
||||
return PRBool(0==mLength);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Getters/Setters...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Retrieve const ptr to internal buffer; DO NOT TRY TO FREE IT!
|
||||
*/
|
||||
const char* GetBuffer(void) const;
|
||||
const PRUnichar* GetUnicode(void) const;
|
||||
|
||||
|
||||
/**
|
||||
* Get nth character.
|
||||
*/
|
||||
PRUnichar operator[](PRUint32 anIndex) const;
|
||||
PRUnichar CharAt(PRUint32 anIndex) const;
|
||||
PRUnichar First(void) const;
|
||||
PRUnichar Last(void) const;
|
||||
|
||||
/**
|
||||
* Set nth character.
|
||||
*/
|
||||
PRBool SetCharAt(PRUnichar aChar,PRUint32 anIndex);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
String concatenation methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Create a new string by appending given string to this
|
||||
* @param aString -- 2nd string to be appended
|
||||
* @return new subsumable string
|
||||
*/
|
||||
nsSubsumeStr operator+(const nsStr& aString);
|
||||
nsSubsumeStr operator+(const nsString& aString);
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given cstring
|
||||
* @param aCString is a ptr to cstring to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeStr operator+(const char* aCString);
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given prunichar*.
|
||||
* @param aString is a ptr to UC-string to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeStr operator+(const PRUnichar* aString);
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given char.
|
||||
* @param aChar is a char to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeStr operator+(char aChar);
|
||||
|
||||
/**
|
||||
* create a new string by adding this to the given char.
|
||||
* @param aChar is a unichar to be added to this
|
||||
* @return newly created string
|
||||
*/
|
||||
nsSubsumeStr operator+(PRUnichar aChar);
|
||||
|
||||
/**********************************************************************
|
||||
Lexomorphic transforms...
|
||||
*********************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase
|
||||
* @update gess 7/27/98
|
||||
*/
|
||||
void ToLowerCase();
|
||||
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase, and
|
||||
* stores them in aOut
|
||||
* @update gess 7/27/98
|
||||
* @param aOut is a string to contain result
|
||||
*/
|
||||
void ToLowerCase(nsString& aString) const;
|
||||
|
||||
/**
|
||||
* Converts chars in this to uppercase
|
||||
* @update gess 7/27/98
|
||||
*/
|
||||
void ToUpperCase();
|
||||
|
||||
/**
|
||||
* Converts chars in this to lowercase, and
|
||||
* stores them in a given output string
|
||||
* @update gess 7/27/98
|
||||
* @param aOut is a string to contain result
|
||||
*/
|
||||
void ToUpperCase(nsString& aString) const;
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to remove all occurances of the
|
||||
* characters found in aSet from this string.
|
||||
*
|
||||
* @param aSet -- characters to be cut from this
|
||||
* @return *this
|
||||
*/
|
||||
nsString& StripChars(const char* aSet);
|
||||
nsString& StripChar(char aChar);
|
||||
|
||||
/**
|
||||
* This method strips whitespace throughout the string
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
nsString& StripWhitespace();
|
||||
|
||||
/**
|
||||
* swaps occurence of 1 string for another
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
nsString& ReplaceChar(PRUnichar anOldChar,PRUnichar aNewChar);
|
||||
nsString& ReplaceChar(const char* aSet,PRUnichar aNewChar);
|
||||
|
||||
PRInt32 CountChar(PRUnichar aChar);
|
||||
|
||||
/**
|
||||
* This method trims characters found in aTrimSet from
|
||||
* either end of the underlying string.
|
||||
*
|
||||
* @param aTrimSet -- contains chars to be trimmed from
|
||||
* both ends
|
||||
* @return this
|
||||
*/
|
||||
nsString& Trim(const char* aSet,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**
|
||||
* This method strips whitespace from string.
|
||||
* You can control whether whitespace is yanked from
|
||||
* start and end of string as well.
|
||||
*
|
||||
* @param aEliminateLeading controls stripping of leading ws
|
||||
* @param aEliminateTrailing controls stripping of trailing ws
|
||||
* @return this
|
||||
*/
|
||||
nsString& CompressSet(const char* aSet, PRUnichar aChar,PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**
|
||||
* This method strips whitespace from string.
|
||||
* You can control whether whitespace is yanked from
|
||||
* start and end of string as well.
|
||||
*
|
||||
* @param aEliminateLeading controls stripping of leading ws
|
||||
* @param aEliminateTrailing controls stripping of trailing ws
|
||||
* @return this
|
||||
*/
|
||||
nsString& CompressWhitespace( PRBool aEliminateLeading=PR_TRUE,PRBool aEliminateTrailing=PR_TRUE);
|
||||
|
||||
/**********************************************************************
|
||||
string conversion methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* This method constructs a new nsString is a clone of this string.
|
||||
*
|
||||
*/
|
||||
nsString* ToNewString() const;
|
||||
|
||||
/**
|
||||
* Creates an ISOLatin1 clone of this string
|
||||
* Note that calls to this method should be matched with calls to Recycle().
|
||||
* @return ptr to new isolatin1 string
|
||||
*/
|
||||
char* ToNewCString() const;
|
||||
|
||||
/**
|
||||
* Creates an UTF8 clone of this string
|
||||
* Note that calls to this method should be matched with calls to Recycle().
|
||||
* @return ptr to new isolatin1 string
|
||||
*/
|
||||
char* ToNewUTF8String() const;
|
||||
|
||||
/**
|
||||
* Creates a unicode clone of this string
|
||||
* Note that calls to this method should be matched with calls to Recycle().
|
||||
* @return ptr to new unicode string
|
||||
*/
|
||||
PRUnichar* ToNewUnicode() const;
|
||||
|
||||
/**
|
||||
* Copies data from internal buffer onto given char* buffer
|
||||
* NOTE: This only copies as many chars as will fit in given buffer (clips)
|
||||
* @param aBuf is the buffer where data is stored
|
||||
* @param aBuflength is the max # of chars to move to buffer
|
||||
* @return ptr to given buffer
|
||||
*/
|
||||
char* ToCString(char* aBuf,PRUint32 aBufLength,PRUint32 anOffset=0) const;
|
||||
|
||||
/**
|
||||
* Perform string to float conversion.
|
||||
* @param aErrorCode will contain error if one occurs
|
||||
* @return float rep of string value
|
||||
*/
|
||||
float ToFloat(PRInt32* aErrorCode) const;
|
||||
|
||||
/**
|
||||
* Try to derive the radix from the value contained in this string
|
||||
* @return kRadix10, kRadix16 or kAutoDetect (meaning unknown)
|
||||
*/
|
||||
PRUint32 DetermineRadix(void);
|
||||
|
||||
/**
|
||||
* Perform string to int conversion.
|
||||
* @param aErrorCode will contain error if one occurs
|
||||
* @return int rep of string value
|
||||
*/
|
||||
PRInt32 ToInteger(PRInt32* aErrorCode,PRUint32 aRadix=kRadix10) const;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
String manipulation methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Functionally equivalent to assign or operator=
|
||||
*
|
||||
*/
|
||||
nsString& SetString(const char* aString,PRInt32 aLength=-1) {return Assign(aString,aLength);}
|
||||
nsString& SetString(const PRUnichar* aString,PRInt32 aLength=-1) {return Assign(aString,aLength);}
|
||||
nsString& SetString(const nsString& aString,PRInt32 aLength=-1) {return Assign(aString,aLength);}
|
||||
|
||||
/**
|
||||
* assign given string to this string
|
||||
* @param aStr: buffer to be assigned to this
|
||||
* @param alength is the length of the given str (or -1)
|
||||
if you want me to determine its length
|
||||
* @return this
|
||||
*/
|
||||
nsString& Assign(const nsStr& aString,PRInt32 aCount=-1);
|
||||
nsString& Assign(const char* aString,PRInt32 aCount=-1);
|
||||
nsString& Assign(const PRUnichar* aString,PRInt32 aCount=-1);
|
||||
nsString& Assign(char aChar);
|
||||
nsString& Assign(PRUnichar aChar);
|
||||
|
||||
/**
|
||||
* here come a bunch of assignment operators...
|
||||
* @param aString: string to be added to this
|
||||
* @return this
|
||||
*/
|
||||
nsString& operator=(const nsString& aString) {return Assign(aString);}
|
||||
nsString& operator=(const nsStr& aString) {return Assign(aString);}
|
||||
nsString& operator=(char aChar) {return Assign(aChar);}
|
||||
nsString& operator=(PRUnichar aChar) {return Assign(aChar);}
|
||||
nsString& operator=(const char* aCString) {return Assign(aCString);}
|
||||
nsString& operator=(const PRUnichar* aString) {return Assign(aString);}
|
||||
#ifdef AIX
|
||||
nsString& operator=(const nsSubsumeStr& aSubsumeString); // AIX requires a const here
|
||||
#else
|
||||
nsString& operator=(nsSubsumeStr& aSubsumeString);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Here's a bunch of methods that append varying types...
|
||||
* @param various...
|
||||
* @return this
|
||||
*/
|
||||
nsString& operator+=(const nsStr& aString){return Append(aString,aString.mLength);}
|
||||
nsString& operator+=(const nsString& aString){return Append(aString,aString.mLength);}
|
||||
nsString& operator+=(const char* aCString) {return Append(aCString);}
|
||||
//nsString& operator+=(char aChar){return Append(aChar);}
|
||||
nsString& operator+=(const PRUnichar* aUCString) {return Append(aUCString);}
|
||||
nsString& operator+=(PRUnichar aChar){return Append(aChar);}
|
||||
|
||||
/*
|
||||
* Appends n characters from given string to this,
|
||||
* This version computes the length of your given string
|
||||
*
|
||||
* @param aString is the source to be appended to this
|
||||
* @return number of chars copied
|
||||
*/
|
||||
nsString& Append(const nsStr& aString) {return Append(aString,aString.mLength);}
|
||||
nsString& Append(const nsString& aString) {return Append(aString,aString.mLength);}
|
||||
|
||||
|
||||
/*
|
||||
* Appends n characters from given string to this,
|
||||
*
|
||||
* @param aString is the source to be appended to this
|
||||
* @param aCount -- number of chars to copy; -1 tells us to compute the strlen for you
|
||||
* @return number of chars copied
|
||||
*/
|
||||
nsString& Append(const nsStr& aString,PRInt32 aCount);
|
||||
nsString& Append(const nsString& aString,PRInt32 aCount);
|
||||
nsString& Append(const char* aString,PRInt32 aCount=-1);
|
||||
nsString& Append(const PRUnichar* aString,PRInt32 aCount=-1);
|
||||
nsString& Append(char aChar);
|
||||
nsString& Append(PRUnichar aChar);
|
||||
nsString& Append(PRInt32 aInteger,PRInt32 aRadix=10); //radix=8,10 or 16
|
||||
nsString& Append(float aFloat);
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at the leftmost offset.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Left(nsString& aCopy,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at the given offset.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @param anOffset -- position where copying begins
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Mid(nsString& aCopy,PRUint32 anOffset,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* Copies n characters from this string to given string,
|
||||
* starting at rightmost char.
|
||||
*
|
||||
*
|
||||
* @param aCopy -- Receiving string
|
||||
* @param aCount -- number of chars to copy
|
||||
* @return number of chars copied
|
||||
*/
|
||||
PRUint32 Right(nsString& aCopy,PRInt32 aCount) const;
|
||||
|
||||
/*
|
||||
* This method inserts n chars from given string into this
|
||||
* string at str[anOffset].
|
||||
*
|
||||
* @param aCopy -- String to be inserted into this
|
||||
* @param anOffset -- insertion position within this str
|
||||
* @param aCount -- number of chars to be copied from aCopy
|
||||
* @return number of chars inserted into this.
|
||||
*/
|
||||
nsString& Insert(const nsString& aCopy,PRUint32 anOffset,PRInt32 aCount=-1);
|
||||
|
||||
/**
|
||||
* Insert a given string into this string at
|
||||
* a specified offset.
|
||||
*
|
||||
* @param aString* to be inserted into this string
|
||||
* @param anOffset is insert pos in str
|
||||
* @return the number of chars inserted into this string
|
||||
*/
|
||||
nsString& Insert(const char* aChar,PRUint32 anOffset,PRInt32 aCount=-1);
|
||||
nsString& Insert(const PRUnichar* aChar,PRUint32 anOffset,PRInt32 aCount=-1);
|
||||
|
||||
/**
|
||||
* Insert a single char into this string at
|
||||
* a specified offset.
|
||||
*
|
||||
* @param character to be inserted into this string
|
||||
* @param anOffset is insert pos in str
|
||||
* @return the number of chars inserted into this string
|
||||
*/
|
||||
//nsString& Insert(char aChar,PRUint32 anOffset);
|
||||
nsString& Insert(PRUnichar aChar,PRUint32 anOffset);
|
||||
|
||||
/*
|
||||
* This method is used to cut characters in this string
|
||||
* starting at anOffset, continuing for aCount chars.
|
||||
*
|
||||
* @param anOffset -- start pos for cut operation
|
||||
* @param aCount -- number of chars to be cut
|
||||
* @return *this
|
||||
*/
|
||||
nsString& Cut(PRUint32 anOffset,PRInt32 aCount);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
Searching methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Search for given character within this string.
|
||||
* This method does so by using a binary search,
|
||||
* so your string HAD BETTER BE ORDERED!
|
||||
*
|
||||
* @param aChar is the unicode char to be found
|
||||
* @return offset in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 BinarySearch(PRUnichar aChar) const;
|
||||
|
||||
/**
|
||||
* Search for given substring within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @param anOffset tells us where in this strig to start searching
|
||||
* @return offset in string, or -1 (kNotFound)
|
||||
*/
|
||||
PRInt32 Find(const nsString& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 Find(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 Find(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 Find(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**
|
||||
* Search for given char within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param anOffset tells us where in this strig to start searching
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @return find pos in string, or -1 (kNotFound)
|
||||
*/
|
||||
//PRInt32 Find(PRUnichar aChar,PRInt32 offset=-1,PRBool aIgnoreCase=PR_FALSE) const;
|
||||
PRInt32 FindChar(PRUnichar aChar,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
/**
|
||||
* This method searches this string for the first character
|
||||
* found in the given charset
|
||||
* @param aString contains set of chars to be found
|
||||
* @param anOffset tells us where to start searching in this
|
||||
* @return -1 if not found, else the offset in this
|
||||
*/
|
||||
PRInt32 FindCharInSet(const char* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 FindCharInSet(const PRUnichar* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 FindCharInSet(const nsStr& aString,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**
|
||||
* This methods scans the string backwards, looking for the given string
|
||||
* @param aString is substring to be sought in this
|
||||
* @param aIgnoreCase tells us whether or not to do caseless compare
|
||||
* @param anOffset tells us where in this strig to start searching (counting from left)
|
||||
*/
|
||||
PRInt32 RFind(const char* aCString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFind(const nsString& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFind(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFind(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**
|
||||
* Search for given char within this string
|
||||
*
|
||||
* @param aString is substring to be sought in this
|
||||
* @param anOffset tells us where in this strig to start searching (counting from left)
|
||||
* @param aIgnoreCase selects case sensitivity
|
||||
* @return find pos in string, or -1 (kNotFound)
|
||||
*/
|
||||
//PRInt32 RFind(PRUnichar aChar,PRInt32 offset=-1,PRBool aIgnoreCase=PR_FALSE) const;
|
||||
PRInt32 RFindChar(PRUnichar aChar,PRBool aIgnoreCase=PR_FALSE,PRInt32 anOffset=-1) const;
|
||||
|
||||
/**
|
||||
* This method searches this string for the last character
|
||||
* found in the given string
|
||||
* @param aString contains set of chars to be found
|
||||
* @param anOffset tells us where in this strig to start searching (counting from left)
|
||||
* @return -1 if not found, else the offset in this
|
||||
*/
|
||||
PRInt32 RFindCharInSet(const char* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFindCharInSet(const PRUnichar* aString,PRInt32 anOffset=-1) const;
|
||||
PRInt32 RFindCharInSet(const nsStr& aString,PRInt32 anOffset=-1) const;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
Comparison methods...
|
||||
*********************************************************************/
|
||||
|
||||
/**
|
||||
* Compares a given string type to this string.
|
||||
* @update gess 7/27/98
|
||||
* @param S is the string to be compared
|
||||
* @param aIgnoreCase tells us how to treat case
|
||||
* @param aCount tells us how many chars to compare
|
||||
* @return -1,0,1
|
||||
*/
|
||||
virtual PRInt32 Compare(const nsString& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
virtual PRInt32 Compare(const nsStr &aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
virtual PRInt32 Compare(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
virtual PRInt32 Compare(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
|
||||
/**
|
||||
* These methods compare a given string type to this one
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator==(const nsString &aString) const;
|
||||
PRBool operator==(const nsStr &aString) const;
|
||||
PRBool operator==(const char *aString) const;
|
||||
PRBool operator==(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods perform a !compare of a given string type to this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE
|
||||
*/
|
||||
PRBool operator!=(const nsString &aString) const;
|
||||
PRBool operator!=(const nsStr &aString) const;
|
||||
PRBool operator!=(const char* aString) const;
|
||||
PRBool operator!=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is < than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator<(const nsString &aString) const;
|
||||
PRBool operator<(const nsStr &aString) const;
|
||||
PRBool operator<(const char* aString) const;
|
||||
PRBool operator<(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is > than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator>(const nsString &aString) const;
|
||||
PRBool operator>(const nsStr &S) const;
|
||||
PRBool operator>(const char* aString) const;
|
||||
PRBool operator>(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is <= than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator<=(const nsString &aString) const;
|
||||
PRBool operator<=(const nsStr &S) const;
|
||||
PRBool operator<=(const char* aString) const;
|
||||
PRBool operator<=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* These methods test if a given string is >= than this
|
||||
* @param aString is the string to be compared to this
|
||||
* @return TRUE or FALSE
|
||||
*/
|
||||
PRBool operator>=(const nsString &aString) const;
|
||||
PRBool operator>=(const nsStr &S) const;
|
||||
PRBool operator>=(const char* aString) const;
|
||||
PRBool operator>=(const PRUnichar* aString) const;
|
||||
|
||||
/**
|
||||
* Compare this to given string; note that we compare full strings here.
|
||||
* The optional length argument just lets us know how long the given string is.
|
||||
* If you provide a length, it is compared to length of this string as an
|
||||
* optimization.
|
||||
*
|
||||
* @param aString -- the string to compare to this
|
||||
* @param aCount -- number of chars to be compared.
|
||||
* @return TRUE if equal
|
||||
*/
|
||||
PRBool Equals(const nsString &aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const nsStr& aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const char* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(const PRUnichar* aString,PRBool aIgnoreCase=PR_FALSE,PRInt32 aCount=-1) const;
|
||||
PRBool Equals(/*FIX: const */nsIAtom* anAtom,PRBool aIgnoreCase) const;
|
||||
PRBool Equals(const PRUnichar* s1, const PRUnichar* s2,PRBool aIgnoreCase=PR_FALSE) const;
|
||||
|
||||
PRBool EqualsIgnoreCase(const nsString& aString) const;
|
||||
PRBool EqualsIgnoreCase(const char* aString,PRInt32 aCount=-1) const;
|
||||
PRBool EqualsIgnoreCase(/*FIX: const */nsIAtom *aAtom) const;
|
||||
PRBool EqualsIgnoreCase(const PRUnichar* s1, const PRUnichar* s2) const;
|
||||
|
||||
/**
|
||||
* Determine if given buffer is plain ascii
|
||||
*
|
||||
* @param aBuffer -- if null, then we test *this, otherwise we test given buffer
|
||||
* @return TRUE if is all ascii chars or if strlen==0
|
||||
*/
|
||||
PRBool IsASCII(const PRUnichar* aBuffer=0);
|
||||
|
||||
|
||||
/**
|
||||
* Determine if given char is a valid space character
|
||||
*
|
||||
* @param aChar is character to be tested
|
||||
* @return TRUE if is valid space char
|
||||
*/
|
||||
static PRBool IsSpace(PRUnichar ch);
|
||||
|
||||
/**
|
||||
* Determine if given char in valid alpha range
|
||||
*
|
||||
* @param aChar is character to be tested
|
||||
* @return TRUE if in alpha range
|
||||
*/
|
||||
static PRBool IsAlpha(PRUnichar ch);
|
||||
|
||||
/**
|
||||
* Determine if given char is valid digit
|
||||
*
|
||||
* @param aChar is character to be tested
|
||||
* @return TRUE if char is a valid digit
|
||||
*/
|
||||
static PRBool IsDigit(PRUnichar ch);
|
||||
|
||||
static void Recycle(nsString* aString);
|
||||
static nsString* CreateString(void);
|
||||
|
||||
};
|
||||
|
||||
extern NS_COM int fputs(const nsString& aString, FILE* out);
|
||||
//ostream& operator<<(ostream& aStream,const nsString& aString);
|
||||
//virtual void DebugDump(ostream& aStream) const;
|
||||
|
||||
|
||||
/**************************************************************
|
||||
Here comes the AutoString class which uses internal memory
|
||||
(typically found on the stack) for its default buffer.
|
||||
If the buffer needs to grow, it gets reallocated on the heap.
|
||||
**************************************************************/
|
||||
|
||||
class NS_COM nsAutoString : public nsString {
|
||||
public:
|
||||
|
||||
nsAutoString();
|
||||
nsAutoString(const char* aCString,PRInt32 aLength=-1);
|
||||
nsAutoString(const PRUnichar* aString,PRInt32 aLength=-1);
|
||||
|
||||
nsAutoString(const CBufDescriptor& aBuffer);
|
||||
nsAutoString(const nsStr& aString);
|
||||
nsAutoString(const nsAutoString& aString);
|
||||
#ifdef AIX
|
||||
nsAutoString(const nsSubsumeStr& aSubsumeStr); // AIX requires a const
|
||||
#else
|
||||
nsAutoString(nsSubsumeStr& aSubsumeStr);
|
||||
#endif // AIX
|
||||
nsAutoString(PRUnichar aChar);
|
||||
virtual ~nsAutoString();
|
||||
|
||||
nsAutoString& operator=(const nsStr& aString) {nsString::Assign(aString); return *this;}
|
||||
nsAutoString& operator=(const nsAutoString& aString) {nsString::Assign(aString); return *this;}
|
||||
nsAutoString& operator=(const char* aCString) {nsString::Assign(aCString); return *this;}
|
||||
nsAutoString& operator=(char aChar) {nsString::Assign(aChar); return *this;}
|
||||
nsAutoString& operator=(const PRUnichar* aBuffer) {nsString::Assign(aBuffer); return *this;}
|
||||
nsAutoString& operator=(PRUnichar aChar) {nsString::Assign(aChar); return *this;}
|
||||
|
||||
/**
|
||||
* Retrieve the size of this string
|
||||
* @return string length
|
||||
*/
|
||||
virtual void SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const;
|
||||
|
||||
char mBuffer[kDefaultStringSize<<eTwoByte];
|
||||
};
|
||||
|
||||
|
||||
|
||||
/***************************************************************
|
||||
The subsumestr class is very unusual.
|
||||
It differs from a normal string in that it doesn't use normal
|
||||
copy semantics when another string is assign to this.
|
||||
Instead, it "steals" the contents of the source string.
|
||||
|
||||
This is very handy for returning nsString classes as part of
|
||||
an operator+(...) for example, in that it cuts down the number
|
||||
of copy operations that must occur.
|
||||
|
||||
You should probably not use this class unless you really know
|
||||
what you're doing.
|
||||
***************************************************************/
|
||||
class NS_COM nsSubsumeStr : public nsString {
|
||||
public:
|
||||
nsSubsumeStr(nsStr& aString);
|
||||
nsSubsumeStr(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength=-1);
|
||||
nsSubsumeStr(char* aString,PRBool assumeOwnership,PRInt32 aLength=-1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
179
mozilla/string/obsolete/nsXPIDLString.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
#include "nsDebug.h"
|
||||
#include "nsIAllocator.h"
|
||||
#include "nsXPIDLString.h"
|
||||
#include "plstr.h"
|
||||
|
||||
// If the allocator changes, fix it here.
|
||||
#define XPIDL_STRING_ALLOC(__len) ((PRUnichar*) nsAllocator::Alloc((__len) * sizeof(PRUnichar)))
|
||||
#define XPIDL_CSTRING_ALLOC(__len) ((char*) nsAllocator::Alloc((__len) * sizeof(char)))
|
||||
#define XPIDL_FREE(__ptr) (nsAllocator::Free(__ptr))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsXPIDLString
|
||||
|
||||
nsXPIDLString::nsXPIDLString()
|
||||
: mBuf(0),
|
||||
mBufOwner(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsXPIDLString::~nsXPIDLString()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
}
|
||||
|
||||
|
||||
nsXPIDLString::operator const PRUnichar*()
|
||||
{
|
||||
return mBuf;
|
||||
}
|
||||
|
||||
|
||||
PRUnichar*
|
||||
nsXPIDLString::Copy(const PRUnichar* aString)
|
||||
{
|
||||
NS_ASSERTION(aString, "null ptr");
|
||||
if (! aString)
|
||||
return 0;
|
||||
|
||||
PRInt32 len = 0;
|
||||
|
||||
{
|
||||
const PRUnichar* p = aString;
|
||||
while (*p++)
|
||||
len++;
|
||||
}
|
||||
|
||||
PRUnichar* result = XPIDL_STRING_ALLOC(len + 1);
|
||||
if (result) {
|
||||
PRUnichar* q = result;
|
||||
while (*aString) {
|
||||
*q = *aString;
|
||||
q++;
|
||||
aString++;
|
||||
}
|
||||
*q = '\0';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PRUnichar**
|
||||
nsXPIDLString::StartAssignmentByValue()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
|
||||
mBuf = 0;
|
||||
mBufOwner = PR_TRUE;
|
||||
return &mBuf;
|
||||
}
|
||||
|
||||
|
||||
const PRUnichar**
|
||||
nsXPIDLString::StartAssignmentByReference()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
|
||||
mBuf = 0;
|
||||
mBufOwner = PR_FALSE;
|
||||
return (const PRUnichar**) &mBuf;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsXPIDLCString
|
||||
|
||||
nsXPIDLCString::nsXPIDLCString()
|
||||
: mBuf(0),
|
||||
mBufOwner(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsXPIDLCString::~nsXPIDLCString()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
}
|
||||
|
||||
|
||||
nsXPIDLCString& nsXPIDLCString::operator =(const char* aCString)
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
|
||||
mBuf = Copy(aCString);
|
||||
mBufOwner = PR_TRUE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
nsXPIDLCString::operator const char*()
|
||||
{
|
||||
return mBuf;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
nsXPIDLCString::Copy(const char* aCString)
|
||||
{
|
||||
NS_ASSERTION(aCString, "null ptr");
|
||||
if (! aCString)
|
||||
return 0;
|
||||
|
||||
PRInt32 len = PL_strlen(aCString);
|
||||
char* result = XPIDL_CSTRING_ALLOC(len + 1);
|
||||
if (result)
|
||||
PL_strcpy(result, aCString);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char**
|
||||
nsXPIDLCString::StartAssignmentByValue()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
|
||||
mBuf = 0;
|
||||
mBufOwner = PR_TRUE;
|
||||
return &mBuf;
|
||||
}
|
||||
|
||||
|
||||
const char**
|
||||
nsXPIDLCString::StartAssignmentByReference()
|
||||
{
|
||||
if (mBufOwner && mBuf)
|
||||
XPIDL_FREE(mBuf);
|
||||
|
||||
mBuf = 0;
|
||||
mBufOwner = PR_FALSE;
|
||||
return (const char**) &mBuf;
|
||||
}
|
||||
|
||||
|
||||
302
mozilla/string/obsolete/nsXPIDLString.h
Normal file
@@ -0,0 +1,302 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public License
|
||||
* Version 1.0 (the "NPL"); you may not use this file except in
|
||||
* compliance with the NPL. You may obtain a copy of the NPL at
|
||||
* http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
||||
* for the specific language governing rights and limitations under the
|
||||
* NPL.
|
||||
*
|
||||
* The Initial Developer of this code under the NPL is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
||||
* Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
A set of string wrapper classes that ease transition to use of XPIDL
|
||||
interfaces. nsXPIDLString and nsXPIDLCString are to XPIDL `wstring'
|
||||
and `string' out params as nsCOMPtr is to generic XPCOM interface
|
||||
pointers. They help you deal with object ownership.
|
||||
|
||||
Consider the following interface:
|
||||
|
||||
interface nsIFoo {
|
||||
attribute string Bar;
|
||||
};
|
||||
|
||||
This will generate the following C++ header file:
|
||||
|
||||
class nsIFoo {
|
||||
NS_IMETHOD SetBar(const PRUnichar* aValue);
|
||||
NS_IMETHOD GetBar(PRUnichar* *aValue);
|
||||
};
|
||||
|
||||
The GetBar() method will allocate a copy of the nsIFoo object's
|
||||
"bar" attribute, and leave you to deal with freeing it:
|
||||
|
||||
nsIFoo* aFoo; // assume we get this somehow
|
||||
PRUnichar* bar;
|
||||
aFoo->GetFoo(&bar);
|
||||
// Use bar here...
|
||||
printf("bar is %s!\n", bar);
|
||||
nsAllocator::Free(bar);
|
||||
|
||||
This makes your life harder, because you need to convolute your code
|
||||
to ensure that you don't leak `bar'.
|
||||
|
||||
Enter nsXPIDLString, which manages the ownership of the allocated
|
||||
string, and automatically destroys it when the nsXPIDLString goes
|
||||
out of scope:
|
||||
|
||||
nsIFoo* aFoo;
|
||||
nsXPIDLString bar;
|
||||
aFoo->GetFoo( getter_Copies(bar) );
|
||||
// Use bar here...
|
||||
printf("bar is %s!\n", (const char*) bar);
|
||||
// no need to remember to nsAllocator::Free().
|
||||
|
||||
Like nsCOMPtr, nsXPIDLString uses some syntactic sugar to make it
|
||||
painfully clear exactly what the code expects. You need to wrap an
|
||||
nsXPIDLString object with either `getter_Copies()' or
|
||||
`getter_Shares()' before passing it to a getter: these tell the
|
||||
nsXPIDLString how ownership is being handled.
|
||||
|
||||
In the case of `getter_Copies()', the callee is allocating a copy
|
||||
(which is usually the case). In the case of `getter_Shares()', the
|
||||
callee is returning a const reference to `the real deal' (this can
|
||||
be done using the [shared] attribute in XPIDL).
|
||||
|
||||
*/
|
||||
|
||||
#ifndef nsXPIDLString_h__
|
||||
#define nsXPIDLString_h__
|
||||
|
||||
#include "nsCom.h"
|
||||
#include "prtypes.h"
|
||||
|
||||
#ifndef __PRUNICHAR__
|
||||
#define __PRUNICHAR__
|
||||
typedef PRUint16 PRUnichar;
|
||||
#endif /* __PRUNICHAR__ */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsXPIDLString
|
||||
//
|
||||
// A wrapper for Unicode strings. With the |getter_Copies()| and
|
||||
// |getter_Shares()| helper functions, this can be used instead of
|
||||
// the "naked" |PRUnichar*| interface for |wstring| parameters in
|
||||
// XPIDL interfaces.
|
||||
//
|
||||
|
||||
class NS_COM nsXPIDLString {
|
||||
private:
|
||||
PRUnichar* mBuf;
|
||||
PRBool mBufOwner;
|
||||
|
||||
PRUnichar** StartAssignmentByValue();
|
||||
const PRUnichar** StartAssignmentByReference();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a new, uninitialized wrapper for a Unicode string.
|
||||
*/
|
||||
nsXPIDLString();
|
||||
|
||||
virtual ~nsXPIDLString();
|
||||
|
||||
/**
|
||||
* Return a reference to the immutable Unicode string.
|
||||
*/
|
||||
operator const PRUnichar*();
|
||||
|
||||
/**
|
||||
* Make a copy of the Unicode string. Use this function in the
|
||||
* callee to ensure that the correct memory allocator is used.
|
||||
*/
|
||||
static PRUnichar* Copy(const PRUnichar* aString);
|
||||
|
||||
// A helper class for assignment-by-value. This class is an
|
||||
// implementation detail and should not be considered part of the
|
||||
// public interface.
|
||||
class NS_COM GetterCopies {
|
||||
private:
|
||||
nsXPIDLString& mXPIDLString;
|
||||
|
||||
public:
|
||||
GetterCopies(nsXPIDLString& aXPIDLString)
|
||||
: mXPIDLString(aXPIDLString) {}
|
||||
|
||||
operator PRUnichar**() {
|
||||
return mXPIDLString.StartAssignmentByValue();
|
||||
}
|
||||
|
||||
friend GetterCopies getter_Copies(nsXPIDLString& aXPIDLString);
|
||||
};
|
||||
|
||||
friend class GetterCopies;
|
||||
|
||||
// A helper class for assignment-by-reference. This class is an
|
||||
// implementation detail and should not be considered part of the
|
||||
// public interface.
|
||||
class NS_COM GetterShares {
|
||||
private:
|
||||
nsXPIDLString& mXPIDLString;
|
||||
|
||||
public:
|
||||
GetterShares(nsXPIDLString& aXPIDLString)
|
||||
: mXPIDLString(aXPIDLString) {}
|
||||
|
||||
operator const PRUnichar**() {
|
||||
return mXPIDLString.StartAssignmentByReference();
|
||||
}
|
||||
|
||||
friend GetterShares getter_Shares(nsXPIDLString& aXPIDLString);
|
||||
};
|
||||
|
||||
friend class GetterShares;
|
||||
|
||||
private:
|
||||
// not to be implemented
|
||||
nsXPIDLString(nsXPIDLString& /* aXPIDLString */) {}
|
||||
nsXPIDLString& operator =(nsXPIDLString& /* aXPIDLString */) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Use this function to "wrap" the nsXPIDLString object that is to
|
||||
* receive an |out| value.
|
||||
*/
|
||||
inline nsXPIDLString::GetterCopies
|
||||
getter_Copies(nsXPIDLString& aXPIDLString)
|
||||
{
|
||||
return nsXPIDLString::GetterCopies(aXPIDLString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function to "wrap" the nsXPIDLString object that is to
|
||||
* receive a |[shared] out| value.
|
||||
*/
|
||||
inline nsXPIDLString::GetterShares
|
||||
getter_Shares(nsXPIDLString& aXPIDLString)
|
||||
{
|
||||
return nsXPIDLString::GetterShares(aXPIDLString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsXPIDLCString
|
||||
//
|
||||
// A wrapper for Unicode strings. With the |getter_Copies()| and
|
||||
// |getter_Shares()| helper functions, this can be used instead of
|
||||
// the "naked" |char*| interface for |string| parameters in XPIDL
|
||||
// interfaces.
|
||||
//
|
||||
|
||||
class NS_COM nsXPIDLCString {
|
||||
private:
|
||||
char* mBuf;
|
||||
PRBool mBufOwner;
|
||||
|
||||
char** StartAssignmentByValue();
|
||||
const char** StartAssignmentByReference();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct a new, uninitialized wrapper for a single-byte string.
|
||||
*/
|
||||
nsXPIDLCString();
|
||||
|
||||
virtual ~nsXPIDLCString();
|
||||
|
||||
/**
|
||||
* Assign a single-byte string to this wrapper. Copies and owns the result.
|
||||
*/
|
||||
nsXPIDLCString& operator =(const char* aString);
|
||||
|
||||
/**
|
||||
* Return a reference to the immutable single-byte string.
|
||||
*/
|
||||
operator const char*();
|
||||
|
||||
/**
|
||||
* Make a copy of the single-byte string. Use this function in the
|
||||
* callee to ensure that the correct memory allocator is used.
|
||||
*/
|
||||
static char* Copy(const char* aString);
|
||||
|
||||
// A helper class for assignment-by-value. This class is an
|
||||
// implementation detail and should not be considered part of the
|
||||
// public interface.
|
||||
class NS_COM GetterCopies {
|
||||
private:
|
||||
nsXPIDLCString& mXPIDLString;
|
||||
|
||||
public:
|
||||
GetterCopies(nsXPIDLCString& aXPIDLString)
|
||||
: mXPIDLString(aXPIDLString) {}
|
||||
|
||||
operator char**() {
|
||||
return mXPIDLString.StartAssignmentByValue();
|
||||
}
|
||||
|
||||
friend GetterCopies getter_Copies(nsXPIDLCString& aXPIDLString);
|
||||
};
|
||||
|
||||
friend class GetterCopies;
|
||||
|
||||
// A helper class for assignment-by-reference. This class is an
|
||||
// implementation detail and should not be considered part of the
|
||||
// public interface.
|
||||
class NS_COM GetterShares {
|
||||
private:
|
||||
nsXPIDLCString& mXPIDLString;
|
||||
|
||||
public:
|
||||
GetterShares(nsXPIDLCString& aXPIDLString)
|
||||
: mXPIDLString(aXPIDLString) {}
|
||||
|
||||
operator const char**() {
|
||||
return mXPIDLString.StartAssignmentByReference();
|
||||
}
|
||||
|
||||
friend GetterShares getter_Shares(nsXPIDLCString& aXPIDLString);
|
||||
};
|
||||
|
||||
friend class GetterShares;
|
||||
|
||||
private:
|
||||
// not to be implemented
|
||||
nsXPIDLCString(nsXPIDLCString& /* aXPIDLString */) {}
|
||||
nsXPIDLCString& operator =(nsXPIDLCString& /* aXPIDLCString */) { return *this; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Use this function to "wrap" the nsXPIDLCString object that is to
|
||||
* receive an |out| value.
|
||||
*/
|
||||
inline nsXPIDLCString::GetterCopies
|
||||
getter_Copies(nsXPIDLCString& aXPIDLString)
|
||||
{
|
||||
return nsXPIDLCString::GetterCopies(aXPIDLString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Use this function to "wrap" the nsXPIDLCString object that is to
|
||||
* receive a |[shared] out| value.
|
||||
*/
|
||||
inline nsXPIDLCString::GetterShares
|
||||
getter_Shares(nsXPIDLCString& aXPIDLString)
|
||||
{
|
||||
return nsXPIDLCString::GetterShares(aXPIDLString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // nsXPIDLString_h__
|
||||
@@ -1,3 +0,0 @@
|
||||
<FilesMatch ^(.*localconfig.*)$>
|
||||
deny from all
|
||||
</FilesMatch>
|
||||
@@ -1,44 +0,0 @@
|
||||
===Litmus Installation Instructions===
|
||||
|
||||
First of all, let me convince you that you in fact probbaly do not want
|
||||
to install Litmus. Litmus is not 1.0 software. Litmus is not even 0.5
|
||||
software. In fact, Litmus doesn't really do any of the things you would
|
||||
expect it to do yet.
|
||||
|
||||
In summary, if you're not looking to install Litmus because you are or
|
||||
want to be a Litmus developer, you'd best turn around and go back from
|
||||
whence you came.
|
||||
|
||||
If at any point you find that you don't understand these installation
|
||||
instructions, it's a good sign you want to turn back as well.
|
||||
|
||||
Required Perl Modules:
|
||||
Class::DBI
|
||||
Class::DBI::mysql
|
||||
Template
|
||||
Time::Piece
|
||||
Time::Piece::mysql
|
||||
Time::Seconds
|
||||
Date::Manip
|
||||
HTML::StripScripts
|
||||
HTML::StripScripts::Parser
|
||||
Text::Markdown
|
||||
XML::XPath
|
||||
|
||||
Once you've got everything installed, run: mysql < createdb.sql to
|
||||
create the Litmus database.
|
||||
|
||||
Then edit Litmus/Config.pm to give it the information it needs to
|
||||
connect to the database.
|
||||
|
||||
Run ./populatedb.pl to create products, testgroups, subgroups, etc...
|
||||
There is no UI at present for doing this.
|
||||
|
||||
Edit the newly created 'localconfig' file and provide your database
|
||||
configuration.
|
||||
|
||||
Then just pop the whole thing into a directory where your web server can
|
||||
get at it. Have fun!
|
||||
|
||||
Note: After upgrading Litmus, it's a good idea to run populatedb.pl
|
||||
again to pick up any schema changes that may have occured.
|
||||
@@ -1,109 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
# Global object store and function library for Litmus
|
||||
|
||||
package Litmus;
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus::Template;
|
||||
use Litmus::Config;
|
||||
use Litmus::Error;
|
||||
use Litmus::Auth;
|
||||
use Litmus::CGI;
|
||||
|
||||
our $_request_cache = {};
|
||||
|
||||
# each cgi _MUST_ call Litmus->init() prior to doing anything else.
|
||||
# init() ensures that the installation has not been disabled, deals with pending
|
||||
# login requests, and other essential tasks.
|
||||
sub init() {
|
||||
if ($Litmus::Config::disabled) {
|
||||
my $c = new CGI();
|
||||
print $c->header();
|
||||
print "Litmus has been shutdown by the administrator. Please try again later.";
|
||||
exit;
|
||||
}
|
||||
# check for pending logins:
|
||||
my $c = cgi();
|
||||
if ($c->param("login_type")) {
|
||||
Litmus::Auth::processLoginForm();
|
||||
}
|
||||
}
|
||||
|
||||
# Global Template object
|
||||
sub template() {
|
||||
my $class = shift;
|
||||
request_cache()->{template} ||= Litmus::Template->create();
|
||||
return request_cache()->{template};
|
||||
}
|
||||
|
||||
# Global CGI object
|
||||
sub cgi() {
|
||||
my $class = shift;
|
||||
request_cache()->{cgi} ||= new Litmus::CGI();
|
||||
return request_cache()->{cgi};
|
||||
}
|
||||
|
||||
sub getCurrentUser {
|
||||
return Litmus::Auth::getCurrentUser();
|
||||
}
|
||||
|
||||
# cache of global variables for a single request only
|
||||
# use me like: Litmus->request_cache->{'var'} = 'foo';
|
||||
# entries here are guarenteed to get flushed when the request ends,
|
||||
# even when running under mod_perl
|
||||
# from Bugzilla.pm:
|
||||
sub request_cache {
|
||||
if ($ENV{MOD_PERL}) {
|
||||
my $request = Apache->request();
|
||||
my $cache = $request->pnotes();
|
||||
# Sometimes mod_perl doesn't properly call DESTROY on all
|
||||
# the objects in pnotes(), so we register a cleanup handler
|
||||
# to make sure that this happens.
|
||||
if (!$cache->{cleanup_registered}) {
|
||||
$request->push_handlers(PerlCleanupHandler => sub {
|
||||
my $r = shift;
|
||||
foreach my $key (keys %{$r->pnotes}) {
|
||||
delete $r->pnotes->{$key};
|
||||
}
|
||||
});
|
||||
$cache->{cleanup_registered} = 1;
|
||||
}
|
||||
return $cache;
|
||||
}
|
||||
return $_request_cache;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,555 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::Auth;
|
||||
|
||||
use strict;
|
||||
|
||||
# IMPORTANT!
|
||||
## You _must_ call Litmus::Auth methods before sending a Content-type
|
||||
## header so that any required cookies can be sent.
|
||||
|
||||
require Exporter;
|
||||
use Litmus::Error;
|
||||
use Time::Piece;
|
||||
use Time::Seconds;
|
||||
use Litmus::DB::User;
|
||||
|
||||
use CGI;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw();
|
||||
|
||||
my $logincookiename = $Litmus::Config::user_cookiename;
|
||||
my $cookie_expire_days = 7;
|
||||
|
||||
# Given a username and password, validate the login. Returns the
|
||||
# Litmus::DB::User object associated with the username if the login
|
||||
# is sucuessful. Returns false otherwise.
|
||||
sub validate_login($$) {
|
||||
my $username = shift;
|
||||
my $password = shift;
|
||||
|
||||
return 0 if (!$username or $username eq '' or
|
||||
!$password or $password eq '');
|
||||
|
||||
my ($userobj) = Litmus::DB::User->search(email => $username);
|
||||
|
||||
if (!$userobj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$userobj->enabled() || $userobj->enabled() == 0) {
|
||||
die "Account ".$userobj->username()." has been disabled by the administrator";
|
||||
}
|
||||
|
||||
# for security reasons, we use the real (correct) crypt'd passwd
|
||||
# as the salt:
|
||||
my $realPasswordCrypted = $userobj->getRealPasswd();
|
||||
return 0 if (!$realPasswordCrypted or $realPasswordCrypted eq '');
|
||||
my $enteredPasswordCrypted = crypt($password, $realPasswordCrypted);
|
||||
if ($enteredPasswordCrypted eq $realPasswordCrypted) {
|
||||
return $userobj;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
# Used by a CGI when a login is required to proceed beyond a certain point.
|
||||
# requireLogin() will return a Litmus::User object to indicate that the user
|
||||
# is logged in, or it will redirect to a login page to allow the login to be
|
||||
# completed. Once the login is complete, the user will be redirected back to
|
||||
# $return_to and any parameters in the current CGI.pm object will be passed
|
||||
# to the new script.
|
||||
#
|
||||
sub requireLogin {
|
||||
my $return_to = shift;
|
||||
my $admin_login_required = shift;
|
||||
my $cgi = Litmus->cgi();
|
||||
|
||||
# see if we are already logged in:
|
||||
my $user = getCurrentUser();
|
||||
return $user if $user; # well, that was easy...
|
||||
|
||||
my $vars = {
|
||||
title => "Login",
|
||||
return_to => $return_to,
|
||||
adminrequired => $admin_login_required,
|
||||
params => $cgi,
|
||||
};
|
||||
|
||||
print $cgi->header();
|
||||
Litmus->template()->process("auth/login.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
# Used by a CGI in much the same way as requireLogin() when the user must
|
||||
# be an admin to proceed.
|
||||
sub requireAdmin {
|
||||
my $return_to = shift;
|
||||
my $cgi = Litmus->cgi();
|
||||
|
||||
my $user = requireLogin($return_to, 1);
|
||||
if (!$user || !$user->is_admin()) {
|
||||
print $cgi->header();
|
||||
basicError("You must be a Litmus administrator to perform this function.");
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
# Returns the current Litmus::DB::Session object corresponding to the current
|
||||
# logged-in user, or 0 if no valid session exists
|
||||
sub getCurrentSession() {
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
# we're actually processing the login form right now, so the cookie hasn't
|
||||
# been sent yet...
|
||||
if (Litmus->request_cache->{'curSession'}) {
|
||||
return Litmus->request_cache->{'curSession'};
|
||||
}
|
||||
|
||||
my $sessionCookie = $c->cookie($logincookiename);
|
||||
if (! $sessionCookie) {
|
||||
return 0
|
||||
}
|
||||
|
||||
my @sessions = Litmus::DB::Session->search(sessioncookie => $sessionCookie);
|
||||
my $session = $sessions[0];
|
||||
if (! $session) { return 0 }
|
||||
|
||||
# see if it's still valid and that the user hasn't been disabled
|
||||
if (! $session->isValid()) { return 0 }
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
# Returns the Litmus::User object corresponding to the current logged-in
|
||||
# user, or 0 if no valid login cookie exists
|
||||
sub getCurrentUser() {
|
||||
my $session = getCurrentSession();
|
||||
|
||||
if ($session) {
|
||||
return $session->user_id();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# ONLY NON-PUBLIC API BEYOND THIS POINT
|
||||
#
|
||||
|
||||
# Processes data from a login form (auth/login.html.tmpl) using data
|
||||
# from the current Litmus::CGI object in use. When complete, returns the
|
||||
# flow of control to the script the user wanted to reach before the login,
|
||||
# setting a login cookie for the user.
|
||||
sub processLoginForm {
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my $type = $c->param("login_type");
|
||||
|
||||
if ($c->param("accountCreated") &&
|
||||
$c->param("accountCreated") eq "true") {
|
||||
# make sure they really are logged in and didn't just set
|
||||
# the accountCreated flag:
|
||||
requireLogin("index.cgi");
|
||||
return; # we're done here
|
||||
}
|
||||
|
||||
if ($type eq "litmus") {
|
||||
my $username = $c->param("email");
|
||||
my $password = $c->param("password");
|
||||
|
||||
my $user = validate_login($username, $password);
|
||||
|
||||
if (!$user) {
|
||||
loginError($c, "Username/Password incorrect. Please try again.");
|
||||
}
|
||||
|
||||
my $session = makeSession($user);
|
||||
$c->storeCookie(makeCookie($session));
|
||||
|
||||
} elsif ($type eq "newaccount") {
|
||||
my $email = $c->param("email");
|
||||
my $name = $c->param("realname");
|
||||
my $password = $c->param("password");
|
||||
my $nickname = $c->param("irc_nickname");
|
||||
|
||||
# some basic form-field validation:
|
||||
my $emailregexp = q:^[\\w\\.\\+\\-=]+@[\\w\\.\\-]+\\.[\\w\\-]+$:;
|
||||
if (! $email || ! $email =~ /$emailregexp/) {
|
||||
loginError($c, "You must enter a valid email address");
|
||||
}
|
||||
if (! $password eq $c->param("password_confirm")) {
|
||||
loginError($c, "Passwords do not match. Please try again.");
|
||||
}
|
||||
|
||||
my @users = Litmus::DB::User->search(email => $email);
|
||||
if ($users[0]) {
|
||||
loginError($c, "User ".$users[0]->email() ." already exists.");
|
||||
}
|
||||
if ($nickname and $nickname ne '') {
|
||||
@users = Litmus::DB::User->search(irc_nickname => $nickname);
|
||||
if ($users[0]) {
|
||||
loginError($c, "An account with that IRC nickname already exists.");
|
||||
}
|
||||
} else {
|
||||
$nickname = undef;
|
||||
}
|
||||
|
||||
my $userobj =
|
||||
Litmus::DB::User->create({email => $email,
|
||||
password => bz_crypt($password),
|
||||
bugzilla_uid => 0,
|
||||
realname => $name,
|
||||
enabled => 1,
|
||||
is_admin => 0,
|
||||
irc_nickname => $nickname
|
||||
});
|
||||
|
||||
my $session = makeSession($userobj);
|
||||
$c->storeCookie(makeCookie($session));
|
||||
|
||||
my $vars = {
|
||||
title => "Account Created",
|
||||
email => $email,
|
||||
realname => $name,
|
||||
return_to => $c->param("login_loc"),
|
||||
params => $c
|
||||
};
|
||||
|
||||
print $c->header();
|
||||
Litmus->template()->process("auth/accountcreated.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit;
|
||||
|
||||
} elsif ($type eq "bugzilla") {
|
||||
my $username = $c->param("username");
|
||||
my $password = $c->param("password");
|
||||
|
||||
|
||||
} elsif ($type eq "convert") {
|
||||
# convert an old-school Litmus account (pre authentication system)
|
||||
# to a new one with a password:
|
||||
my $username = $c->param("email");
|
||||
|
||||
my @userobjs = Litmus::DB::User->search(email => $username);
|
||||
my $userobj = $userobjs[0];
|
||||
if (! $userobj || $userobj->email() ne $username) {
|
||||
loginError($c, "User $username does not exist.");
|
||||
}
|
||||
|
||||
if ($userobj->password() ne "") {
|
||||
# already a new-style account
|
||||
loginError($c, "User $username has already been updated.");
|
||||
}
|
||||
|
||||
# display a "convert login" form to get the rest of the information
|
||||
print $c->header();
|
||||
my $return_to = $c->param("login_loc") || "";
|
||||
unsetFields();
|
||||
my $vars = {
|
||||
title => "Update Login",
|
||||
email => $username,
|
||||
return_to => $return_to,
|
||||
params => $c,
|
||||
};
|
||||
|
||||
Litmus->template()->process("auth/convertaccount.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
exit;
|
||||
} elsif ($type eq "reallyconvert") {
|
||||
my $username = $c->param("email");
|
||||
my $realname = $c->param("realname");
|
||||
my $password = $c->param("password");
|
||||
my $password_confirm = $c->param("password_confirm");
|
||||
my $nickname = $c->param("irc_nickname");
|
||||
|
||||
if (! $password eq $password_confirm) {
|
||||
loginError($c, "Passwords do not match. Please try again.");
|
||||
}
|
||||
|
||||
my @userobjs = Litmus::DB::User->search(email => $username);
|
||||
my $userobj = $userobjs[0];
|
||||
|
||||
# just to be safe:
|
||||
if (! $userobj || $userobj->email() ne $username) {
|
||||
loginError($c, "User $username does not exist.");
|
||||
}
|
||||
|
||||
if ($userobj->password() ne "" && ($userobj->bugzilla_uid() == 0 ||
|
||||
!$userobj->bugzilla_uid())) {
|
||||
# already a new-style account
|
||||
loginError($c, "User $username has already been updated.");
|
||||
}
|
||||
my ($nickname_exists) =
|
||||
Litmus::DB::User->search(irc_nickname => $nickname);
|
||||
if ($nickname_exists) {
|
||||
loginError($c,
|
||||
"An account with that IRC nickname already exists.",
|
||||
"auth/convertaccount.html.tmpl",
|
||||
"Update Login");
|
||||
}
|
||||
|
||||
# do the upgrade:
|
||||
$userobj->password(bz_crypt($password));
|
||||
$userobj->bugzilla_uid("0");
|
||||
$userobj->realname($realname);
|
||||
$userobj->enabled(1);
|
||||
# $userobj->is_admin(0);
|
||||
$userobj->irc_nickname($nickname);
|
||||
$userobj->update();
|
||||
|
||||
my $session = makeSession($userobj);
|
||||
$c->storeCookie(makeCookie($session));
|
||||
|
||||
my $vars = {
|
||||
title => "Account Created",
|
||||
email => $username,
|
||||
realname => $realname,
|
||||
return_to => $c->param("login_loc"),
|
||||
params => $c
|
||||
};
|
||||
|
||||
print $c->header();
|
||||
Litmus->template()->process("auth/accountcreated.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit;
|
||||
|
||||
} else {
|
||||
internalError("Unknown login scheme attempted");
|
||||
}
|
||||
}
|
||||
|
||||
sub changePassword {
|
||||
my $userobj = shift;
|
||||
my $password = shift;
|
||||
$userobj->password(bz_crypt($password));
|
||||
$userobj->update();
|
||||
|
||||
my @sessions = $userobj->sessions();
|
||||
foreach my $session (@sessions) {
|
||||
$session->makeExpire();
|
||||
}
|
||||
}
|
||||
|
||||
# Given a userobj, process the login and return a session object
|
||||
sub makeSession {
|
||||
my $userobj = shift;
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my $expires = localtime() + ONE_DAY * $cookie_expire_days;
|
||||
|
||||
my $sessioncookie = randomToken(64);
|
||||
|
||||
my $session = Litmus::DB::Session->create({
|
||||
user_id => $userobj,
|
||||
sessioncookie => $sessioncookie,
|
||||
expires => $expires});
|
||||
|
||||
Litmus->request_cache->{'curSession'} = $session;
|
||||
return $session;
|
||||
}
|
||||
|
||||
# Given a session, create a login cookie to go with it
|
||||
sub makeCookie {
|
||||
my $session = shift;
|
||||
|
||||
my $cookie = Litmus->cgi()->cookie(
|
||||
-name => $logincookiename,
|
||||
-value => $session->sessioncookie(),
|
||||
-domain => $main::ENV{"HTTP_HOST"},
|
||||
-expires => $session->expires(),
|
||||
);
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
sub loginError {
|
||||
my $c = shift;
|
||||
my $message = shift;
|
||||
my $template = shift;
|
||||
my $title = shift;
|
||||
|
||||
if (!$template) {
|
||||
$template = "auth/login.html.tmpl";
|
||||
}
|
||||
if (!$title) {
|
||||
$title = "Login";
|
||||
}
|
||||
|
||||
print $c->header();
|
||||
|
||||
my $return_to = $c->param("login_loc") || "";
|
||||
my $email = $c->param("email") || "";
|
||||
|
||||
unsetFields();
|
||||
|
||||
my $vars = {
|
||||
title => $title,
|
||||
return_to => $return_to,
|
||||
email => $email,
|
||||
params => $c,
|
||||
onload => "toggleMessage('failure','$message')",
|
||||
};
|
||||
|
||||
Litmus->template()->process($template, $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
sub unsetFields() {
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
# We need to unset some params in $c since otherwise we end up with
|
||||
# a hidden form field set for "email" and friends and madness results:
|
||||
$c->param('email', '');
|
||||
$c->param('username', '');
|
||||
$c->param('login_type', '');
|
||||
$c->param('login_loc', '');
|
||||
$c->param('realname', '');
|
||||
$c->param('password', '');
|
||||
$c->param('password_confirm', '');
|
||||
|
||||
}
|
||||
|
||||
# Like crypt(), but with a random salt. Thanks to Bugzilla for this.
|
||||
sub bz_crypt {
|
||||
my ($password) = @_;
|
||||
|
||||
# The list of characters that can appear in a salt. Salts and hashes
|
||||
# are both encoded as a sequence of characters from a set containing
|
||||
# 64 characters, each one of which represents 6 bits of the salt/hash.
|
||||
# The encoding is similar to BASE64, the difference being that the
|
||||
# BASE64 plus sign (+) is replaced with a forward slash (/).
|
||||
my @saltchars = (0..9, 'A'..'Z', 'a'..'z', '.', '/');
|
||||
|
||||
# Generate the salt. We use an 8 character (48 bit) salt for maximum
|
||||
# security on systems whose crypt uses MD5. Systems with older
|
||||
# versions of crypt will just use the first two characters of the salt.
|
||||
my $salt = '';
|
||||
for ( my $i=0 ; $i < 8 ; ++$i ) {
|
||||
$salt .= $saltchars[rand(64)];
|
||||
}
|
||||
|
||||
# Crypt the password.
|
||||
my $cryptedpassword = crypt($password, $salt);
|
||||
|
||||
# Return the crypted password.
|
||||
return $cryptedpassword;
|
||||
}
|
||||
|
||||
sub randomToken {
|
||||
my $size = shift || 10; # default to 10 chars if nothing specified
|
||||
return join("", map{ ('0'..'9','a'..'z','A'..'Z')[rand 62] } (1..$size));
|
||||
}
|
||||
|
||||
# Deprecated:
|
||||
# DO NOT USE
|
||||
sub setCookie {
|
||||
my $user = shift;
|
||||
my $expires = shift;
|
||||
|
||||
my $user_id = 0;
|
||||
if ($user) {
|
||||
$user_id = $user->user_id();
|
||||
}
|
||||
|
||||
if (!$expires or $expires eq '') {
|
||||
$expires = '+3d';
|
||||
}
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my $cookie = $c->cookie(
|
||||
-name => $logincookiename,
|
||||
-value => $user_id,
|
||||
-domain => $main::ENV{"HTTP_HOST"},
|
||||
-expires => $expires,
|
||||
);
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
# Deprecated:
|
||||
sub getCookie() {
|
||||
return getCurrentUser();
|
||||
}
|
||||
|
||||
sub istrusted($) {
|
||||
my $userobj = shift;
|
||||
|
||||
return 0 if (!$userobj);
|
||||
|
||||
if ($userobj->istrusted()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub canEdit($) {
|
||||
my $userobj = shift;
|
||||
|
||||
return $userobj->istrusted();
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# logout()
|
||||
#
|
||||
# Unset the user's cookie
|
||||
#########################################################################
|
||||
sub logout() {
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my $cookie = $c->cookie(
|
||||
-name => $logincookiename,
|
||||
-value => '',
|
||||
-domain => $main::ENV{"HTTP_HOST"},
|
||||
-expires => '-1d'
|
||||
);
|
||||
$c->storeCookie($cookie);
|
||||
|
||||
# invalidate the session behind the cookie as well:
|
||||
my $session = getCurrentSession();
|
||||
if ($session) { $session->makeExpire() }
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::BugzillaDBI;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Litmus::Config;
|
||||
use Litmus::Error;
|
||||
use Memoize;
|
||||
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
BEGIN {
|
||||
unless ($Litmus::Config::bugzilla_auth_enabled) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $dsn = "dbi:mysql:$Litmus::Config::bugzilla_db:$Litmus::Config::bugzilla_host";
|
||||
|
||||
Litmus::BugzillaDBI->set_db('Main',
|
||||
$dsn,
|
||||
$Litmus::Config::bugzilla_user,
|
||||
$Litmus::Config::bugzilla_pass);
|
||||
|
||||
|
||||
|
||||
1;
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::CGI;
|
||||
|
||||
use strict;
|
||||
use base 'CGI';
|
||||
|
||||
# Subclass of CGI.pm that can store cookies in advance and output them
|
||||
# later when the header() method is called. This feature should probably
|
||||
# be submitted as a patch to CGI.pm itself.
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my @args = @_;
|
||||
|
||||
my $self = $class->SUPER::new(@args);
|
||||
$self->{'litmusCookieStore'} = [];
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Stores a cookie to be set later by the header() method
|
||||
sub storeCookie {
|
||||
my $self = shift;
|
||||
my $cookie = shift;
|
||||
|
||||
# "we're like kids in a candy shop"
|
||||
my @cookieStore = @{$self->{'litmusCookieStore'}};
|
||||
push(@cookieStore, $cookie);
|
||||
$self->{'litmusCookieStore'} = \@cookieStore;
|
||||
}
|
||||
|
||||
sub header {
|
||||
my $self = shift;
|
||||
my @args = @_;
|
||||
|
||||
foreach my $cur ($self->{'litmusCookieStore'}) {
|
||||
push(@args, {-cookie => $cur});
|
||||
}
|
||||
|
||||
$self->SUPER::header(@args);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,125 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::Cache;
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Data::JavaScript;
|
||||
use Litmus::DB::Product;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
@Litmus::Cache::EXPORT = qw(
|
||||
rebuildCache
|
||||
);
|
||||
|
||||
# generate a new litmusconfig.js file because something has updated:
|
||||
sub rebuildCache {
|
||||
unless (-e "data") { system("mkdir", "data") }
|
||||
open(CACHE, ">data/litmusconfig.js.new");
|
||||
|
||||
print CACHE "// Litmus configuration information\n";
|
||||
print CACHE "// Do not edit this file directly. It is autogenerated from the database\n";
|
||||
print CACHE "\n";
|
||||
|
||||
# we build up @litmusconfig, a big perl data structure, and spit the
|
||||
# whole thing out as javascript with Data::JavaScript
|
||||
# shield your eyes, we're in for a long trip
|
||||
my @litmusconfig;
|
||||
my @products = Litmus::DB::Product->search(enabled => 1);
|
||||
my $prodcount = 0;
|
||||
foreach my $curproduct (@products) {
|
||||
my $prodrow = \%{$litmusconfig[$prodcount]};
|
||||
$prodrow->{"name"} = $curproduct->name();
|
||||
$prodrow->{"id"} = $curproduct->product_id();
|
||||
|
||||
my $brancharray = \@{$prodrow->{"branches"}};
|
||||
my $branchcount = 0;
|
||||
foreach my $curbranch ($curproduct->branches(enabled => 1)) {
|
||||
my $branchrow = \%{$brancharray->[$branchcount]};
|
||||
$branchrow->{"name"} = $curbranch->name();
|
||||
$branchrow->{"id"} = $curbranch->branch_id();
|
||||
$branchcount++;
|
||||
}
|
||||
|
||||
my $platformarray = \@{$prodrow->{"platforms"}};
|
||||
my $platformcount = 0;
|
||||
foreach my $curplat (Litmus::DB::Platform->search_ByProduct($curproduct->product_id())) {
|
||||
my $platrow = \%{$platformarray->[$platformcount]};
|
||||
$platrow->{"name"} = $curplat->name();
|
||||
$platrow->{"id"} = $curplat->platform_id();
|
||||
|
||||
my $opsysarray = \@{$platrow->{"opsyses"}};
|
||||
my $opsyscount = 0;
|
||||
foreach my $curopsys ($curplat->opsyses()) {
|
||||
my $opsysrow = \%{$opsysarray->[$opsyscount]};
|
||||
$opsysrow->{"name"} = $curopsys->name();
|
||||
$opsysrow->{"id"} = $curopsys->opsys_id();
|
||||
$opsyscount++;
|
||||
}
|
||||
$platformcount++;
|
||||
}
|
||||
|
||||
my $grouparray = \@{$prodrow->{"testgroups"}};
|
||||
my $groupcount = 0;
|
||||
foreach my $curgroup ($curproduct->testgroups()) {
|
||||
next if (!$curgroup->enabled);
|
||||
my $grouprow = \%{$grouparray->[$groupcount]};
|
||||
$grouprow->{"name"} = $curgroup->name();
|
||||
$grouprow->{"id"} = $curgroup->testgroup_id();
|
||||
$groupcount++;
|
||||
|
||||
my $subgrouparray = \@{$grouprow->{"subgroups"}};
|
||||
my $subgroupcount = 0;
|
||||
foreach my $cursubgroup (Litmus::DB::Subgroup->search_EnabledByTestgroup($curgroup->testgroup_id())) {
|
||||
next if (!$cursubgroup->enabled);
|
||||
my $subgrouprow = \%{$subgrouparray->[$subgroupcount]};
|
||||
$subgrouprow->{"name"} = $cursubgroup->name();
|
||||
$subgrouprow->{"id"} = $cursubgroup->subgroup_id();
|
||||
$subgroupcount++;
|
||||
}
|
||||
}
|
||||
|
||||
$prodcount++;
|
||||
}
|
||||
|
||||
|
||||
my $data = jsdump('litmusconfig', \@litmusconfig);
|
||||
$data =~ s/new Object;/new Array\(\);/g;
|
||||
print CACHE $data;
|
||||
close(CACHE);
|
||||
system("mv", "data/litmusconfig.js.new", "data/litmusconfig.js");
|
||||
#system("chmod", "-R", "755", "data/");
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::Config;
|
||||
|
||||
use strict;
|
||||
|
||||
do 'localconfig';
|
||||
|
||||
our $version = "0.6";
|
||||
|
||||
# if true, then Litmus will not accept any requests
|
||||
our $disabled = 0;
|
||||
|
||||
our $datadir = "data/";
|
||||
|
||||
# Set/unset this to display inline debugging value/code.
|
||||
our $DEBUG = 0;
|
||||
|
||||
1;
|
||||
@@ -1,48 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Branch;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Branch->table('branches');
|
||||
|
||||
Litmus::DB::Branch->columns(All => qw/branch_id product_id name detect_regexp enabled/);
|
||||
Litmus::DB::Branch->columns(Essential => qw/branch_id product_id name detect_regexp enabled/);
|
||||
|
||||
Litmus::DB::Branch->column_alias("product_id", "product");
|
||||
|
||||
Litmus::DB::Branch->has_many(test_results => "Litmus::DB::Testresult");
|
||||
Litmus::DB::Branch->has_a(product => "Litmus::DB::Product");
|
||||
|
||||
1;
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::BugzillaUser;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::BugzillaDBI';
|
||||
|
||||
Litmus::DB::BugzillaUser->table('profiles');
|
||||
|
||||
Litmus::DB::BugzillaUser->columns(All => qw/userid login_name cryptpassword realname
|
||||
disabledtext mybugslink refreshed_when extern_id/);
|
||||
|
||||
|
||||
1;
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::BuildType;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::BuildType->table('build_type_lookup');
|
||||
|
||||
Litmus::DB::BuildType->columns(All => qw/build_type_id name/);
|
||||
|
||||
Litmus::DB::BuildType->has_many(test_results => "Litmus::DB::Testresult");
|
||||
|
||||
1;
|
||||
@@ -1,53 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Comment;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Time::Piece;
|
||||
|
||||
Litmus::DB::Comment->table('test_result_comments');
|
||||
|
||||
Litmus::DB::Comment->columns(All => qw/comment_id test_result_id last_updated submission_time user_id comment/);
|
||||
|
||||
Litmus::DB::Comment->column_alias("test_result_id", "test_result");
|
||||
Litmus::DB::Comment->column_alias("user_id", "user");
|
||||
|
||||
Litmus::DB::Comment->has_a(test_result => "Litmus::DB::Testresult");
|
||||
Litmus::DB::Comment->has_a(user => "Litmus::DB::User");
|
||||
|
||||
Litmus::DB::Comment->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
1;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::ExitStatus;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::ExitStatus->table('exit_status_lookup');
|
||||
|
||||
Litmus::DB::ExitStatus->columns(All => qw/exit_status_id name/);
|
||||
|
||||
Litmus::DB::ExitStatus->has_many(test_results => "Litmus::DB::Testresult");
|
||||
|
||||
1;
|
||||
@@ -1,46 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Format;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Format->table('test_format_lookup');
|
||||
|
||||
Litmus::DB::Format->columns(All => qw/format_id name/);
|
||||
|
||||
Litmus::DB::Format->column_alias("format_id", "formatid");
|
||||
|
||||
Litmus::DB::Format->has_many(testcases => "Litmus::DB::Testcase");
|
||||
|
||||
1;
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Locale;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Locale->table('locale_lookup');
|
||||
|
||||
Litmus::DB::Locale->columns(All => qw/abbrev name/);
|
||||
|
||||
Litmus::DB::Locale->column_alias("abbrev", "locale");
|
||||
|
||||
Litmus::DB::Locale->has_many(test_results => "Litmus::DB::Testresult");
|
||||
|
||||
__PACKAGE__->set_sql(RetrieveAll => qq{
|
||||
SELECT __ESSENTIAL__
|
||||
FROM __TABLE__
|
||||
ORDER BY abbrev ASC
|
||||
});
|
||||
|
||||
1;
|
||||
@@ -1,50 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Log;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Log->table('test_result_logs');
|
||||
|
||||
Litmus::DB::Log->columns(All => qw/log_id last_updated submission_time log_type_id log_text/);
|
||||
|
||||
Litmus::DB::Log->column_alias("test_results", "testresults");
|
||||
Litmus::DB::Log->column_alias("log_type_id", "log_type");
|
||||
|
||||
Litmus::DB::Log->has_a(log_type => "Litmus::DB::LogType");
|
||||
Litmus::DB::Log->has_many(test_results => ["Litmus::DB::LogTestresult" => 'test_result']);
|
||||
|
||||
Litmus::DB::Testresult->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
1;
|
||||
@@ -1,47 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::LogTestresult;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::LogTestresult->table('testresult_logs_join');
|
||||
|
||||
Litmus::DB::LogTestresult->columns(Primary => qw/test_result_id log_id/);
|
||||
|
||||
Litmus::DB::LogTestresult->column_alias("test_result_id", "test_result");
|
||||
|
||||
Litmus::DB::LogTestresult->has_a(test_result => "Litmus::DB::Testresult");
|
||||
Litmus::DB::LogTestresult->has_a(log_id => "Litmus::DB::Log");
|
||||
|
||||
1;
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::LogType;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::LogType->table('log_type_lookup');
|
||||
|
||||
Litmus::DB::LogType->columns(All => qw/log_type_id name/);
|
||||
|
||||
Litmus::DB::LogType->has_many(test_result_logs => "Litmus::DB::Log");
|
||||
|
||||
1;
|
||||
@@ -1,48 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Opsys;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Opsys->table('opsyses');
|
||||
|
||||
Litmus::DB::Opsys->columns(All => qw/opsys_id platform_id name detect_regexp/);
|
||||
Litmus::DB::Opsys->columns(Essential => qw/opsys_id platform_id name detect_regexp/);
|
||||
|
||||
Litmus::DB::Opsys->column_alias("platform_id", "platform");
|
||||
|
||||
Litmus::DB::Opsys->has_many(test_results => "Litmus::DB::Testresult");
|
||||
Litmus::DB::Opsys->has_a(platform => "Litmus::DB::Platform");
|
||||
|
||||
1;
|
||||
@@ -1,105 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Platform;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
use CGI;
|
||||
|
||||
Litmus::DB::Platform->table('platforms');
|
||||
|
||||
Litmus::DB::Platform->columns(All => qw/platform_id name detect_regexp iconpath/);
|
||||
Litmus::DB::Platform->columns(Essential => qw/platform_id name detect_regexp iconpath/);
|
||||
|
||||
Litmus::DB::Platform->column_alias("platform_id", "platformid");
|
||||
|
||||
Litmus::DB::Platform->has_many(opsyses => "Litmus::DB::Opsys");
|
||||
|
||||
Litmus::DB::Platform->set_sql(ByProduct => qq{
|
||||
SELECT pl.*
|
||||
FROM platforms pl, platform_products plpr
|
||||
WHERE plpr.product_id=? AND plpr.platform_id=pl.platform_id
|
||||
});
|
||||
|
||||
Litmus::DB::Platform->set_sql(ByProductAndName => qq{
|
||||
SELECT pl.*
|
||||
FROM platforms pl, platform_products plpr
|
||||
WHERE plpr.product_id=? AND plpr.platform_id=pl.platform_id
|
||||
AND pl.name=?
|
||||
});
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_products() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from platform_products WHERE platform_id=?";
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->platform_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_with_refs() {
|
||||
my $self = shift;
|
||||
|
||||
$self->delete_from_products();
|
||||
|
||||
# XXX: How to handle opsyses?
|
||||
|
||||
return $self->delete;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_products() {
|
||||
my $self = shift;
|
||||
my $new_product_ids = shift;
|
||||
|
||||
if (scalar @$new_product_ids) {
|
||||
# Failing to delete products is _not_ fatal when adding a new subgroup.
|
||||
my $rv = $self->delete_from_products();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO platform_products (platform_id,product_id) VALUES (?,?)";
|
||||
foreach my $new_product_id (@$new_product_ids) {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->platform_id,
|
||||
$new_product_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
@@ -1,108 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Product;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Product->table('products');
|
||||
|
||||
Litmus::DB::Product->columns(All => qw/product_id name iconpath enabled/);
|
||||
Litmus::DB::Product->columns(Essential => qw/product_id name iconpath enabled/);
|
||||
|
||||
Litmus::DB::Product->column_alias("product_id", "productid");
|
||||
|
||||
Litmus::DB::Product->has_many(testcases => "Litmus::DB::Testcase",
|
||||
{ order_by => 'testcase_id' });
|
||||
Litmus::DB::Product->has_many(subgroups => "Litmus::DB::Subgroup",
|
||||
{ order_by => 'name' });
|
||||
Litmus::DB::Product->has_many(testgroups => "Litmus::DB::Testgroup",
|
||||
{ order_by => 'name' });
|
||||
Litmus::DB::Product->has_many(branches => "Litmus::DB::Branch",
|
||||
{ order_by => 'name' });
|
||||
|
||||
__PACKAGE__->set_sql(ByPlatform => qq{
|
||||
SELECT pr.*
|
||||
FROM products pr, platform_products plpr
|
||||
WHERE plpr.platform_id=? AND plpr.product_id=pr.product_id
|
||||
ORDER BY pr.name ASC
|
||||
});
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_platforms() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from platform_products WHERE product_id=?";
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->product_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_with_refs() {
|
||||
my $self = shift;
|
||||
$self->delete_from_platforms();
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "UPDATE testgroups SET product_id=0,enabled=0 WHERE product_id=?";
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->product_id
|
||||
);
|
||||
|
||||
# Remove references to product in other entities.
|
||||
# Disable those entities for good measure.
|
||||
$sql = "UPDATE subgroups SET product_id=0,enabled=0 WHERE product_id=?";
|
||||
$rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->product_id
|
||||
);
|
||||
|
||||
$sql = "UPDATE testcases SET product_id=0,enabled=0,community_enabled=0 WHERE product_id=?";
|
||||
$rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->product_id
|
||||
);
|
||||
|
||||
$sql = "UPDATE branches SET product_id=0,enabled=0 WHERE product_id=?";
|
||||
$rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->product_id
|
||||
);
|
||||
|
||||
return $self->delete;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::ResultStatus;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::ResultStatus->table('test_result_status_lookup');
|
||||
|
||||
Litmus::DB::ResultStatus->columns(All => qw/result_status_id name class_name/);
|
||||
|
||||
Litmus::DB::ResultStatus->has_many(test_results => "Litmus::DB::Testresult");
|
||||
|
||||
1;
|
||||
@@ -1,54 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Resultbug;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Time::Piece;
|
||||
|
||||
Litmus::DB::Resultbug->table('test_result_bugs');
|
||||
|
||||
Litmus::DB::Resultbug->columns(Primary => qw/test_result_id bug_id/);
|
||||
Litmus::DB::Resultbug->columns(All => qw/last_updated submission_time user_id/);
|
||||
|
||||
Litmus::DB::Resultbug->column_alias("test_result_id", "test_result");
|
||||
Litmus::DB::Resultbug->column_alias("test_result_id", "testresult");
|
||||
Litmus::DB::Resultbug->column_alias("user_id", "user");
|
||||
|
||||
Litmus::DB::Resultbug->has_a(test_result => "Litmus::DB::Testresult");
|
||||
Litmus::DB::Resultbug->has_a(user => "Litmus::DB::User");
|
||||
|
||||
Litmus::DB::Resultbug->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
1;
|
||||
@@ -1,69 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Session;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Time::Piece;
|
||||
|
||||
Litmus::DB::Session->table('sessions');
|
||||
|
||||
Litmus::DB::Session->columns(All => qw/session_id user_id sessioncookie expires/);
|
||||
|
||||
Litmus::DB::Session->has_a(user_id => "Litmus::DB::User");
|
||||
|
||||
Litmus::DB::Session->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
# expire the current Session object
|
||||
sub makeExpire {
|
||||
my $self = shift;
|
||||
$self->delete();
|
||||
}
|
||||
|
||||
sub isValid {
|
||||
my $self = shift;
|
||||
|
||||
if ($self->expires() <= localtime()) {
|
||||
$self->makeExpire();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$self->user_id()->enabled() || $self->user_id()->enabled() == 0) {
|
||||
$self->makeExpire();
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,306 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Subgroup;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Time::Piece::MySQL;
|
||||
#use Litmus::DB::Testresult;
|
||||
|
||||
Litmus::DB::Subgroup->table('subgroups');
|
||||
|
||||
Litmus::DB::Subgroup->columns(All => qw/subgroup_id name testrunner_group_id enabled product_id/);
|
||||
Litmus::DB::Subgroup->columns(Essential => qw/subgroup_id name testrunner_group_id enabled product_id/);
|
||||
|
||||
Litmus::DB::Subgroup->column_alias("subgroup_id", "subgroupid");
|
||||
|
||||
Litmus::DB::Subgroup->has_a(product => "Litmus::DB::Product");
|
||||
|
||||
__PACKAGE__->set_sql(EnabledByTestgroup => qq{
|
||||
SELECT sg.*
|
||||
FROM subgroups sg, subgroup_testgroups sgtg
|
||||
WHERE sgtg.testgroup_id=? AND sgtg.subgroup_id=sg.subgroup_id AND sg.enabled=1
|
||||
ORDER BY sgtg.sort_order ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(ByTestgroup => qq{
|
||||
SELECT sg.*
|
||||
FROM subgroups sg, subgroup_testgroups sgtg
|
||||
WHERE sgtg.testgroup_id=? AND sgtg.subgroup_id=sg.subgroup_id
|
||||
ORDER BY sgtg.sort_order ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(NumEnabledTestcases => qq{
|
||||
SELECT count(tc.testcase_id) as num_testcases
|
||||
FROM testcases tc, testcase_subgroups tcsg
|
||||
WHERE tcsg.subgroup_id=? AND tcsg.testcase_id=tc.testcase_id AND tc.enabled=1 AND tc.community_enabled=1
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(EnabledByTestcase => qq{
|
||||
SELECT sg.*
|
||||
FROM subgroups sg, testcase_subgroups tcsg
|
||||
WHERE tcsg.testcase_id=? AND tcsg.subgroup_id=sg.subgroup_id AND sg.enabled=1
|
||||
ORDER by sg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(ByTestcase => qq{
|
||||
SELECT sg.*
|
||||
FROM subgroups sg, testcase_subgroups tcsg
|
||||
WHERE tcsg.testcase_id=? AND tcsg.subgroup_id=sg.subgroup_id
|
||||
ORDER by sg.name ASC
|
||||
});
|
||||
|
||||
#########################################################################
|
||||
sub coverage() {
|
||||
my $self = shift;
|
||||
my $platform = shift;
|
||||
my $build_id = shift;
|
||||
my $locale = shift;
|
||||
my $community_only = shift;
|
||||
my $user = shift;
|
||||
|
||||
my $sql = "SELECT COUNT(t.testcase_id) FROM testcase_subgroups tsg, testcases t WHERE tsg.subgroup_id=? AND tsg.testcase_id=t.testcase_id AND t.enabled=1";
|
||||
if ($community_only) {
|
||||
$sql .= " AND t.community_enabled=1";
|
||||
}
|
||||
my $dbh = $self->db_Main();
|
||||
my $sth = $dbh->prepare_cached($sql);
|
||||
$sth->execute(
|
||||
$self->{'subgroup_id'},
|
||||
);
|
||||
my ($num_testcases) = $sth->fetchrow_array;
|
||||
|
||||
$sth->finish;
|
||||
|
||||
if (!$num_testcases or
|
||||
$num_testcases == 0) { return "N/A" }
|
||||
|
||||
|
||||
$sql = "SELECT t.testcase_id, count(tr.testresult_id) AS num_results
|
||||
FROM testcase_subgroups tsg JOIN testcases t ON (tsg.testcase_id=t.testcase_id) LEFT JOIN test_results tr ON (tr.testcase_id=t.testcase_id) JOIN opsyses o ON (tr.opsys_id=o.opsys_id)
|
||||
WHERE tsg.subgroup_id=? AND tr.build_id=? AND tr.locale_abbrev=? AND o.platform_id=?";
|
||||
if ($community_only) {
|
||||
$sql .= " AND t.community_enabled=1";
|
||||
}
|
||||
if ($user) {
|
||||
$sql .= " AND tr.user_id=" . $user->{'user_id'};
|
||||
}
|
||||
|
||||
$sql .= " GROUP BY tr.testcase_id";
|
||||
|
||||
$sth = $dbh->prepare_cached($sql);
|
||||
$sth->execute(
|
||||
$self->{'subgroup_id'},
|
||||
$build_id,
|
||||
$locale,
|
||||
$platform->{'platform_id'}
|
||||
);
|
||||
my @test_results = $self->sth_to_objects($sth);
|
||||
|
||||
$sth->finish;
|
||||
|
||||
if (@test_results == 0) { return "0" }
|
||||
|
||||
my $num_completed = 0;
|
||||
foreach my $curtest (@test_results) {
|
||||
if ($curtest->{'num_results'} > 0) {
|
||||
$num_completed++;
|
||||
}
|
||||
}
|
||||
|
||||
my $result = $num_completed/($num_testcases) * 100;
|
||||
unless ($result) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
return sprintf("%d",$result);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub clone() {
|
||||
my $self = shift;
|
||||
|
||||
my $new_subgroup = $self->copy;
|
||||
if (!$new_subgroup) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Propagate testgroup membership;
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO subgroup_testgroups (subgroup_id,testgroup_id,sort_order) SELECT ?,testgroup_id,sort_order FROM subgroup_testgroups WHERE subgroup_id=?";
|
||||
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_subgroup->subgroup_id,
|
||||
$self->subgroup_id
|
||||
);
|
||||
if (! $rows) {
|
||||
# XXX: Do we need to throw a warning here?
|
||||
# What happens when we clone a subgroup that doesn't belong to
|
||||
# any testgroups?
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO testcase_subgroups (testcase_id,subgroup_id,sort_order) SELECT testcase_id,?,sort_order FROM testcase_subgroups WHERE subgroup_id=?";
|
||||
|
||||
$rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_subgroup->subgroup_id,
|
||||
$self->subgroup_id
|
||||
);
|
||||
if (! $rows) {
|
||||
# XXX: Do we need to throw a warning here?
|
||||
}
|
||||
|
||||
return $new_subgroup;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_testgroups() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from subgroup_testgroups WHERE subgroup_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->subgroup_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_testgroup() {
|
||||
my $self = shift;
|
||||
my $testgroup_id = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from subgroup_testgroups WHERE subgroup_id=? AND testgroup_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->subgroup_id,
|
||||
$testgroup_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_testcases() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from testcase_subgroups WHERE subgroup_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->subgroup_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_with_refs() {
|
||||
my $self = shift;
|
||||
$self->delete_from_testgroups();
|
||||
$self->delete_from_testcases();
|
||||
return $self->delete;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_testgroups() {
|
||||
my $self = shift;
|
||||
my $new_testgroup_ids = shift;
|
||||
|
||||
if (scalar @$new_testgroup_ids) {
|
||||
# Failing to delete testgroups is _not_ fatal when adding a new subgroup.
|
||||
my $rv = $self->delete_from_testgroups();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO subgroup_testgroups (subgroup_id,testgroup_id,sort_order) VALUES (?,?,1)";
|
||||
foreach my $new_testgroup_id (@$new_testgroup_ids) {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->subgroup_id,
|
||||
$new_testgroup_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_testgroup() {
|
||||
my $self = shift;
|
||||
my $testgroup_id = shift;
|
||||
my $sort_order = shift;
|
||||
|
||||
# Sort order defaults to 1.
|
||||
if (!$sort_order) {
|
||||
$sort_order = 1;
|
||||
}
|
||||
|
||||
my $rv = $self->delete_from_testgroup($testgroup_id);
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO subgroup_testgroups (subgroup_id,testgroup_id,sort_order) VALUES (?,?,?)";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->subgroup_id,
|
||||
$testgroup_id,
|
||||
$sort_order
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_testcases() {
|
||||
my $self = shift;
|
||||
my $new_testcase_ids = shift;
|
||||
|
||||
if (scalar @$new_testcase_ids) {
|
||||
# Failing to delete testcases is _not_ fatal when adding a new subgroup.
|
||||
my $rv = $self->delete_from_testcases();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testcase_subgroups (testcase_id,subgroup_id,sort_order) VALUES (?,?,?)";
|
||||
my $sort_order = 1;
|
||||
foreach my $new_testcase_id (@$new_testcase_ids) {
|
||||
next if (!$new_testcase_id);
|
||||
# Log any failures/duplicate keys to STDERR.
|
||||
eval {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_testcase_id,
|
||||
$self->subgroup_id,
|
||||
$sort_order
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR $@;
|
||||
}
|
||||
$sort_order++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::TestRun;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Time::Piece;
|
||||
use Time::Seconds;
|
||||
|
||||
Litmus::DB::TestRun->table('test_runs');
|
||||
|
||||
Litmus::DB::TestRun->columns(All => qw/test_run_id testgroup_id name description start_timestamp finish_timestamp enabled/);
|
||||
|
||||
Litmus::DB::TestRun->column_alias("testgroup_id", "testgroup");
|
||||
|
||||
Litmus::DB::TestRun->has_a(testgroup => "Litmus::DB::Testgroup");
|
||||
Litmus::DB::TestRun->has_many(subgroups =>
|
||||
[ "Litmus::DB::SubgroupTestgroup" => "testgroup" ]);
|
||||
|
||||
Litmus::DB::TestRun->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Testcase;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Date::Manip;
|
||||
#use Litmus::DB::Testresult;
|
||||
use Memoize;
|
||||
#use Litmus::Error;
|
||||
|
||||
our $default_relevance_threshold = 1.0;
|
||||
our $default_match_limit = 25;
|
||||
our $default_num_days = 7;
|
||||
|
||||
Litmus::DB::Testcase->table('testcases');
|
||||
|
||||
Litmus::DB::Testcase->columns(Primary => qw/testcase_id/);
|
||||
Litmus::DB::Testcase->columns(Essential => qw/summary details enabled community_enabled format_id regression_bug_id product_id steps expected_results author_id creation_date last_updated version testrunner_case_id testrunner_case_version/);
|
||||
|
||||
Litmus::DB::Testcase->column_alias("testcase_id", "testid");
|
||||
Litmus::DB::Testcase->column_alias("testcase_id", "test_id");
|
||||
Litmus::DB::Testcase->column_alias("testcase_id", "id");
|
||||
Litmus::DB::Testcase->column_alias("community_enabled", "communityenabled");
|
||||
Litmus::DB::Testcase->column_alias("format_id", "format");
|
||||
Litmus::DB::Testcase->column_alias("author_id", "author");
|
||||
Litmus::DB::Testcase->column_alias("product_id", "product");
|
||||
|
||||
Litmus::DB::Testcase->has_a("format" => "Litmus::DB::Format");
|
||||
Litmus::DB::Testcase->has_a("author" => "Litmus::DB::User");
|
||||
Litmus::DB::Testcase->has_a("product" => "Litmus::DB::Product");
|
||||
|
||||
__PACKAGE__->set_sql(EnabledBySubgroup => qq{
|
||||
SELECT t.*
|
||||
FROM testcases t, testcase_subgroups tsg
|
||||
WHERE tsg.subgroup_id=? AND tsg.testcase_id=t.testcase_id AND t.enabled=1
|
||||
ORDER BY tsg.sort_order ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(BySubgroup => qq{
|
||||
SELECT t.*
|
||||
FROM testcases t, testcase_subgroups tsg
|
||||
WHERE
|
||||
tsg.subgroup_id=? AND
|
||||
tsg.testcase_id=t.testcase_id
|
||||
ORDER BY tsg.sort_order ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(ByTestgroup => qq{
|
||||
SELECT t.*
|
||||
FROM testcases t, testcase_subgroups tsg, subgroup_testgroups sgtg
|
||||
WHERE
|
||||
tsg.testcase_id=t.testcase_id AND
|
||||
tsg.subgroup_id=sgtg.subgroup_id AND
|
||||
sgtg.testgroup_id = ?
|
||||
ORDER BY tsg.sort_order ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(CommunityEnabledBySubgroup => qq{
|
||||
SELECT t.*
|
||||
FROM testcases t, testcase_subgroups tsg
|
||||
WHERE tsg.subgroup_id=? AND tsg.testcase_id=t.testcase_id AND t.enabled=1 AND t.community_enabled=1
|
||||
ORDER BY tsg.sort_order ASC, t.testcase_id ASC
|
||||
});
|
||||
|
||||
Litmus::DB::Testcase->has_many(test_results => "Litmus::DB::Testresult", {order_by => 'submission_time DESC'});
|
||||
|
||||
Litmus::DB::Testcase->has_many(subgroups =>
|
||||
["Litmus::DB::TestcaseSubgroup" => 'subgroup']);
|
||||
|
||||
#########################################################################
|
||||
# is_completed($$$$$)
|
||||
#
|
||||
# Check whether we have test results for the current test that correspond
|
||||
# to the provided platform, build_id, and user(optional).
|
||||
#########################################################################
|
||||
memoize('is_completed');
|
||||
sub is_completed {
|
||||
my $self = shift;
|
||||
my $platform = shift;
|
||||
my $build_id = shift;
|
||||
my $locale = shift;
|
||||
my $user = shift; # optional
|
||||
|
||||
my @results;
|
||||
if ($user) {
|
||||
@results = Litmus::DB::Testresult->search_CompletedByUser(
|
||||
$self->{'testcase_id'},
|
||||
$build_id,
|
||||
$locale->{'abbrev'},
|
||||
$platform->{'platform_id'},
|
||||
$user->{'user_id'},
|
||||
);
|
||||
} else {
|
||||
@results = Litmus::DB::Testresult->search_Completed(
|
||||
$self->{'testcase_id'},
|
||||
$build_id,
|
||||
$locale->{'abbrev'},
|
||||
$platform->{'platform_id'},
|
||||
);
|
||||
}
|
||||
|
||||
return @results;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getFullTextMatches() {
|
||||
my $self = shift;
|
||||
my $text_snippet = shift;
|
||||
my $match_limit = shift;
|
||||
my $relevance_threshold = shift;
|
||||
|
||||
if (!$match_limit) {
|
||||
$match_limit = $default_match_limit;
|
||||
}
|
||||
if (!$relevance_threshold) {
|
||||
$relevance_threshold = $default_relevance_threshold
|
||||
}
|
||||
|
||||
__PACKAGE__->set_sql(FullTextMatches => qq{
|
||||
SELECT testcase_id, summary, creation_date, last_updated, MATCH (summary,steps,expected_results) AGAINST (?) AS relevance
|
||||
FROM testcases
|
||||
WHERE MATCH (summary,steps,expected_results) AGAINST (?) HAVING relevance > ?
|
||||
ORDER BY relevance DESC, summary ASC
|
||||
LIMIT $match_limit
|
||||
});
|
||||
|
||||
|
||||
return $self->search_FullTextMatches(
|
||||
$text_snippet,
|
||||
$text_snippet,
|
||||
$relevance_threshold
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getNewTestcases() {
|
||||
my $self = shift;
|
||||
my $num_days = shift;
|
||||
my $match_limit = shift;
|
||||
|
||||
if (!$num_days) {
|
||||
$num_days = $default_num_days;
|
||||
}
|
||||
|
||||
if (!$match_limit) {
|
||||
$match_limit = $default_match_limit;
|
||||
}
|
||||
|
||||
__PACKAGE__->set_sql(NewTestcases => qq{
|
||||
SELECT testcase_id, summary, creation_date, last_updated
|
||||
FROM testcases
|
||||
WHERE creation_date>=?
|
||||
ORDER BY creation_date DESC
|
||||
LIMIT $match_limit
|
||||
});
|
||||
|
||||
my $err;
|
||||
my $new_datestamp=&UnixDate(DateCalc("now","- $num_days days"),"%q");
|
||||
return $self->search_NewTestcases($new_datestamp);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getRecentlyUpdated() {
|
||||
my $self = shift;
|
||||
my $num_days = shift;
|
||||
my $match_limit = shift;
|
||||
|
||||
if (!$num_days) {
|
||||
$num_days = $default_num_days;
|
||||
}
|
||||
|
||||
if (!$match_limit) {
|
||||
$match_limit = $default_match_limit;
|
||||
}
|
||||
|
||||
__PACKAGE__->set_sql(RecentlyUpdated => qq{
|
||||
SELECT testcase_id, summary, creation_date, last_updated
|
||||
FROM testcases
|
||||
WHERE last_updated>=? AND last_updated>creation_date
|
||||
ORDER BY last_updated DESC
|
||||
LIMIT $match_limit
|
||||
});
|
||||
|
||||
my $err;
|
||||
my $new_datestamp=&UnixDate(DateCalc("now","- $num_days days"),"%q");
|
||||
return $self->search_RecentlyUpdated($new_datestamp);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getDefaultMatchLimit() {
|
||||
return $default_match_limit;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getDefaultRelevanceThreshold() {
|
||||
return $default_relevance_threshold;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getDefaultNumDays() {
|
||||
return $default_num_days;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub clone() {
|
||||
my $self = shift;
|
||||
|
||||
my $new_testcase = $self->copy;
|
||||
if (!$new_testcase) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Update dates to now.
|
||||
my $now = &UnixDate("today","%q");
|
||||
$new_testcase->creation_date($now);
|
||||
$new_testcase->last_updated($now);
|
||||
$new_testcase->update();
|
||||
|
||||
# Propagate subgroup membership;
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testcase_subgroups (testcase_id,subgroup_id,sort_order) SELECT ?,subgroup_id,sort_order FROM testcase_subgroups WHERE testcase_id=?";
|
||||
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_testcase->testcase_id,
|
||||
$self->testcase_id
|
||||
);
|
||||
if (! $rows) {
|
||||
# XXX: Do we need to throw a warning here?
|
||||
# What happens when we clone a testcase that doesn't belong to
|
||||
# any subgroups?
|
||||
}
|
||||
|
||||
$sql = "INSERT INTO related_testcases (testcase_id, related_testcase_id) VALUES (?,?)";
|
||||
$rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id,
|
||||
$new_testcase->testcase_id
|
||||
);
|
||||
if (! $rows) {
|
||||
# XXX: Do we need to throw a warning here?
|
||||
}
|
||||
|
||||
return $new_testcase;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_subgroups() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from testcase_subgroups WHERE testcase_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_subgroup() {
|
||||
my $self = shift;
|
||||
my $subgroup_id = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from testcase_subgroups WHERE testcase_id=? AND subgroup_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id,
|
||||
$subgroup_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_related() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from related_testcases WHERE testcase_id=? OR related_testcase_id=?";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id,
|
||||
$self->testcase_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_with_refs() {
|
||||
my $self = shift;
|
||||
$self->delete_from_subgroups();
|
||||
$self->delete_from_related();
|
||||
return $self->delete;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_subgroups() {
|
||||
my $self = shift;
|
||||
my $new_subgroup_ids = shift;
|
||||
|
||||
if (scalar @$new_subgroup_ids) {
|
||||
# Failing to delete subgroups is _not_ fatal when adding a new testcase.
|
||||
my $rv = $self->delete_from_subgroups();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testcase_subgroups (testcase_id,subgroup_id,sort_order) VALUES (?,?,1)";
|
||||
foreach my $new_subgroup_id (@$new_subgroup_ids) {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id,
|
||||
$new_subgroup_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_subgroup() {
|
||||
my $self = shift;
|
||||
my $subgroup_id = shift;
|
||||
my $sort_order = shift;
|
||||
|
||||
# Sort order defaults to 1.
|
||||
if (!$sort_order) {
|
||||
$sort_order = 1;
|
||||
}
|
||||
|
||||
my $rv = $self->delete_from_subgroup($subgroup_id);
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testcase_subgroups (testcase_id,subgroup_id,sort_order) VALUES (?,?,?)";
|
||||
return $dbh->do($sql,
|
||||
undef,
|
||||
$self->testcase_id,
|
||||
$subgroup_id,
|
||||
$sort_order
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::TestcaseSubgroup;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::TestcaseSubgroup->table('testcase_subgroups');
|
||||
|
||||
Litmus::DB::TestcaseSubgroup->columns(All => qw/testcase_id subgroup_id/);
|
||||
|
||||
Litmus::DB::TestcaseSubgroup->has_a(testcase_id => "Litmus::DB::Testcase");
|
||||
Litmus::DB::TestcaseSubgroup->has_a(subgroup_id => "Litmus::DB::Subgroup");
|
||||
|
||||
Litmus::DB::TestcaseSubgroup->column_alias("testcase_id", "testcase");
|
||||
Litmus::DB::TestcaseSubgroup->column_alias("testcase_id", "test");
|
||||
Litmus::DB::TestcaseSubgroup->column_alias("subgroup_id", "subgroup");
|
||||
|
||||
1;
|
||||
@@ -1,260 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Testgroup;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::Testgroup->table('testgroups');
|
||||
|
||||
Litmus::DB::Testgroup->columns(All => qw/testgroup_id product_id name enabled testrunner_plan_id/);
|
||||
Litmus::DB::Testgroup->columns(Essential => qw/testgroup_id product_id name enabled testrunner_plan_id/);
|
||||
|
||||
Litmus::DB::Testgroup->column_alias("product_id", "product");
|
||||
|
||||
Litmus::DB::Testgroup->has_a(product => "Litmus::DB::Product");
|
||||
|
||||
__PACKAGE__->set_sql(EnabledByBranch => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, testgroup_branches tgb
|
||||
WHERE tgb.branch_id=? AND tgb.testgroup_id=tg.testgroup_id AND tg.enabled=1 ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(ByBranch => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, testgroup_branches tgb
|
||||
WHERE tgb.branch_id=? AND tgb.testgroup_id=tg.testgroup_id ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(EnabledBySubgroup => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, subgroup_testgroups sgtg
|
||||
WHERE sgtg.subgroup_id=? AND sgtg.testgroup_id=tg.testgroup_id AND tg.enabled=1 ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(BySubgroup => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, subgroup_testgroups sgtg
|
||||
WHERE sgtg.subgroup_id=? AND sgtg.testgroup_id=tg.testgroup_id ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(EnabledByTestcase => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, subgroup_testgroups sgtg, testcase_subgroups tcsg
|
||||
WHERE tcsg.testcase_id=? AND tcsg.subgroup_id=sgtg.subgroup_id AND sgtg.testgroup_id=tg.testgroup_id AND tg.enabled=1 ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(ByTestcase => qq{
|
||||
SELECT tg.*
|
||||
FROM testgroups tg, subgroup_testgroups sgtg, testcase_subgroups tcsg
|
||||
WHERE tcsg.testcase_id=? AND tcsg.subgroup_id=sgtg.subgroup_id AND sgtg.testgroup_id=tg.testgroup_id ORDER by tg.name ASC
|
||||
});
|
||||
|
||||
#########################################################################
|
||||
sub coverage {
|
||||
my $self = shift;
|
||||
my $platform = shift;
|
||||
my $build_id = shift;
|
||||
my $locale = shift;
|
||||
my $community_only = shift;
|
||||
my $user = shift;
|
||||
|
||||
my $percent_completed = 0;
|
||||
|
||||
my @subgroups = Litmus::DB::Subgroup->search_EnabledByTestgroup($self->testgroup_id);
|
||||
my $num_empty_subgroups = 0;
|
||||
foreach my $subgroup (@subgroups) {
|
||||
my $subgroup_percent = $subgroup->coverage(
|
||||
$platform,
|
||||
$build_id,
|
||||
$locale,
|
||||
$community_only,
|
||||
$user,
|
||||
);
|
||||
if ($subgroup_percent eq "N/A") {
|
||||
$num_empty_subgroups++;
|
||||
} else {
|
||||
$percent_completed += $subgroup_percent;
|
||||
}
|
||||
}
|
||||
|
||||
if (scalar(@subgroups) - $num_empty_subgroups == 0) {
|
||||
return "N/A"
|
||||
}
|
||||
my $total_percentage = $percent_completed /
|
||||
(scalar @subgroups - $num_empty_subgroups);
|
||||
|
||||
return sprintf("%d",$total_percentage);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub clone() {
|
||||
my $self = shift;
|
||||
|
||||
my $new_testgroup = $self->copy;
|
||||
if (!$new_testgroup) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Propagate testgroup membership;
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO subgroup_testgroups (subgroup_id,testgroup_id,sort_order) SELECT subgroup_id,?,sort_order FROM subgroup_testgroups WHERE testgroup_id=?";
|
||||
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_testgroup->testgroup_id,
|
||||
$self->testgroup_id
|
||||
);
|
||||
if (! $rows) {
|
||||
# XXX: Do we need to throw a warning here?
|
||||
}
|
||||
|
||||
return $new_testgroup;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_subgroups() {
|
||||
my $self = shift;
|
||||
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "DELETE from subgroup_testgroups WHERE testgroup_id=?";
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->testgroup_id
|
||||
);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_from_test_runs() {
|
||||
my $self = shift;
|
||||
|
||||
# XXX: Placeholder for test runs.
|
||||
return;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub delete_with_refs() {
|
||||
my $self = shift;
|
||||
$self->delete_from_subgroups();
|
||||
$self->delete_from_test_runs();
|
||||
return $self->delete;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_subgroups() {
|
||||
my $self = shift;
|
||||
my $new_subgroup_ids = shift;
|
||||
|
||||
if (scalar @$new_subgroup_ids) {
|
||||
# Failing to delete subgroups is _not_ fatal when adding a new testgroup.
|
||||
my $rv = $self->delete_from_subgroups();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO subgroup_testgroups (subgroup_id,testgroup_id,sort_order) VALUES (?,?,?)";
|
||||
my $sort_order = 1;
|
||||
foreach my $new_subgroup_id (@$new_subgroup_ids) {
|
||||
next if (!$new_subgroup_id);
|
||||
# Log any failures/duplicate keys to STDERR.
|
||||
eval {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$new_subgroup_id,
|
||||
$self->testgroup_id,
|
||||
$sort_order
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR $@;
|
||||
}
|
||||
$sort_order++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub update_branches() {
|
||||
my $self = shift;
|
||||
my $new_branch_ids = shift;
|
||||
|
||||
if (scalar @$new_branch_ids) {
|
||||
# Failing to delete branches is _not_ fatal when adding a new testgroup.
|
||||
my $rv = $self->delete_from_branches();
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testgroup_branches (testgroup_id,branch_id) VALUES (?,?)";
|
||||
foreach my $new_branch_id (@$new_branch_ids) {
|
||||
next if (!$new_branch_id);
|
||||
# Log any failures/duplicate keys to STDERR.
|
||||
eval {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->testgroup_id,
|
||||
$new_branch_id,
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR $@;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub add_branch() {
|
||||
my $self = shift;
|
||||
my $new_branch_id = shift;
|
||||
|
||||
# Failure to insert isn't fatal in the case of a collision, but we do want
|
||||
# to log it.
|
||||
my $dbh = __PACKAGE__->db_Main();
|
||||
my $sql = "INSERT INTO testgroup_branches (testgroup_id,branch_id) VALUES (?,?)";
|
||||
# Log any failures/duplicate keys to STDERR.
|
||||
eval {
|
||||
my $rows = $dbh->do($sql,
|
||||
undef,
|
||||
$self->testgroup_id,
|
||||
$new_branch_id,
|
||||
);
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR $@;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::Testresult;
|
||||
|
||||
use strict;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
use Date::Manip;
|
||||
use Time::Piece;
|
||||
use Time::Seconds;
|
||||
use Memoize;
|
||||
|
||||
our $_num_results_default = 15;
|
||||
|
||||
Litmus::DB::Testresult->table('test_results');
|
||||
|
||||
Litmus::DB::Testresult->columns(All => qw/testresult_id testcase_id last_updated submission_time user_id opsys_id branch_id build_id user_agent result_status_id build_type_id machine_name exit_status_id duration_ms talkback_id valid vetted validated_by_user_id vetted_by_user_id validated_timestamp vetted_timestamp locale_abbrev is_automated_result/);
|
||||
|
||||
Litmus::DB::Testresult->column_alias("testcase_id", "testcase");
|
||||
Litmus::DB::Testresult->column_alias("submission_time", "timestamp");
|
||||
Litmus::DB::Testresult->column_alias("user_id", "user");
|
||||
Litmus::DB::Testresult->column_alias("opsys_id", "opsys");
|
||||
Litmus::DB::Testresult->column_alias("branch_id", "branch");
|
||||
Litmus::DB::Testresult->column_alias("user_agent", "useragent");
|
||||
Litmus::DB::Testresult->column_alias("result_status_id", "result_status");
|
||||
Litmus::DB::Testresult->column_alias("build_type_id", "build_type");
|
||||
Litmus::DB::Testresult->column_alias("exit_status_id", "exit_status");
|
||||
Litmus::DB::Testresult->column_alias("validity_id", "validity");
|
||||
Litmus::DB::Testresult->column_alias("vetting_status_id", "vetting_status");
|
||||
Litmus::DB::Testresult->column_alias("locale_abbrev", "locale");
|
||||
Litmus::DB::Testresult->column_alias("is_automated_result", "isAutomated");
|
||||
|
||||
Litmus::DB::Testresult->has_a(opsys => "Litmus::DB::Opsys");
|
||||
Litmus::DB::Testresult->has_a(branch => "Litmus::DB::Branch");
|
||||
Litmus::DB::Testresult->has_a(testcase => "Litmus::DB::Testcase");
|
||||
Litmus::DB::Testresult->has_a(result_status => "Litmus::DB::ResultStatus");
|
||||
Litmus::DB::Testresult->has_a(user => "Litmus::DB::User");
|
||||
Litmus::DB::Testresult->has_a(useragent => "Litmus::UserAgentDetect");
|
||||
Litmus::DB::Testresult->has_a(build_type => "Litmus::DB::BuildType");
|
||||
Litmus::DB::Testresult->has_a(exit_status => "Litmus::DB::ExitStatus");
|
||||
Litmus::DB::Testresult->has_a(locale => "Litmus::DB::Locale");
|
||||
Litmus::DB::Testresult->has_a(platform =>
|
||||
[ "Litmus::DB::Opsys" => "platform" ]);
|
||||
|
||||
Litmus::DB::Testresult->has_many(logs =>
|
||||
["Litmus::DB::LogTestresult" => 'log_id']);
|
||||
Litmus::DB::Testresult->has_many(comments => "Litmus::DB::Comment", {order_by => 'comment_id ASC, submission_time ASC'});
|
||||
Litmus::DB::Testresult->has_many(bugs => "Litmus::DB::Resultbug", {order_by => 'bug_id ASC, submission_time DESC'});
|
||||
|
||||
Litmus::DB::Testresult->autoinflate(dates => 'Time::Piece');
|
||||
|
||||
Litmus::DB::Testresult->set_sql(DefaultTestResults => qq{
|
||||
SELECT tr.testresult_id,tr.testcase_id,t.summary,tr.submission_time AS created,p.name AS platform_name,pr.name as product_name,trsl.name AS result_status,trsl.class_name result_status_class,b.name AS branch_name,tg.name AS test_group_name, tr.locale_abbrev, u.email
|
||||
FROM test_results tr, testcases t, platforms p, opsyses o, branches b, products pr, test_result_status_lookup trsl, testgroups tg, subgroups sg, users u, testcase_subgroups tcsg, subgroup_testgroups sgtg
|
||||
WHERE tr.testcase_id=t.testcase_id AND tr.opsys_id=o.opsys_id AND o.platform_id=p.platform_id AND tr.branch_id=b.branch_id AND b.product_id=pr.product_id AND tr.result_status_id=trsl.result_status_id AND tcsg.testcase_id=tr.testcase_id AND tcsg.subgroup_id=sg.subgroup_id AND sg.subgroup_id=sgtg.subgroup_id AND sgtg.testgroup_id=tg.testgroup_id AND tr.user_id=u.user_id AND tr.valid=1
|
||||
ORDER BY tr.submission_time DESC, t.testcase_id DESC
|
||||
LIMIT $_num_results_default
|
||||
});
|
||||
|
||||
Litmus::DB::Testresult->set_sql(CommonResults => qq{
|
||||
SELECT COUNT(tr.testcase_id) AS num_results, tr.testcase_id, t.summary, MAX(tr.submission_time) AS most_recent, MAX(tr.testresult_id) AS max_id
|
||||
FROM test_results tr, testcases t, test_result_status_lookup trsl
|
||||
WHERE tr.testcase_id=t.testcase_id AND tr.result_status_id=trsl.result_status_id AND trsl.class_name=?
|
||||
GROUP BY tr.testcase_id
|
||||
ORDER BY num_results DESC, tr.testresult_id DESC
|
||||
LIMIT 15
|
||||
});
|
||||
|
||||
Litmus::DB::Testresult->set_sql(Completed => qq{
|
||||
SELECT tr.*
|
||||
FROM test_results tr, opsyses o
|
||||
WHERE tr.testcase_id=? AND
|
||||
tr.build_id=? AND
|
||||
tr.locale_abbrev=? AND
|
||||
tr.opsys_id=o.opsys_id AND
|
||||
o.platform_id=?
|
||||
ORDER BY tr.submission_time DESC
|
||||
});
|
||||
|
||||
Litmus::DB::Testresult->set_sql(CompletedByUser => qq{
|
||||
SELECT tr.*
|
||||
FROM test_results tr, opsyses o
|
||||
WHERE tr.testcase_id=? AND
|
||||
tr.build_id=? AND
|
||||
tr.locale_abbrev=? AND
|
||||
tr.opsys_id=o.opsys_id AND
|
||||
o.platform_id=? AND
|
||||
tr.user_id=?
|
||||
ORDER BY tr.submission_time DESC
|
||||
});
|
||||
|
||||
#########################################################################
|
||||
# for historical reasons, note() is a shorthand way of saying "the text of
|
||||
# the first comment on this result if that comment was submitted by the
|
||||
# result submitter"
|
||||
#########################################################################
|
||||
sub note {
|
||||
my $self = shift;
|
||||
|
||||
my @comments = $self->comments();
|
||||
|
||||
if (@comments && $comments[0] &&
|
||||
$comments[0]->user() == $self->user()) {
|
||||
return $comments[0]->comment();
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# is this test result from a trusted user?
|
||||
#########################################################################
|
||||
sub istrusted {
|
||||
my $self = shift;
|
||||
|
||||
if ($self->user()->istrusted()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# &getDefaultTestResults($)
|
||||
#
|
||||
#########################################################################
|
||||
sub getDefaultTestResults($) {
|
||||
my $self = shift;
|
||||
my @rows = $self->search_DefaultTestResults();
|
||||
my $criteria = "Default<br/>Ordered by Created<br/>Limit to $_num_results_default results";
|
||||
return $criteria, \@rows;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# &getTestResults($\@\@$)
|
||||
#
|
||||
#########################################################################
|
||||
sub getTestResults($\@\@$) {
|
||||
my ($self,$where_criteria,$order_by_criteria,$limit_value) = @_;
|
||||
|
||||
my $select = 'SELECT tr.testresult_id,tr.testcase_id,t.summary,tr.submission_time AS created,p.name AS platform_name,pr.name as product_name,trsl.name AS result_status,trsl.class_name AS result_status_class,b.name AS branch_name,tg.name AS test_group_name, tr.locale_abbrev, u.email';
|
||||
|
||||
my $from = 'FROM test_results tr, testcases t, platforms p, opsyses o, branches b, products pr, test_result_status_lookup trsl, testgroups tg, subgroups sg, users u, testcase_subgroups tcsg, subgroup_testgroups sgtg';
|
||||
|
||||
my $where = 'WHERE tr.testcase_id=t.testcase_id AND tr.opsys_id=o.opsys_id AND o.platform_id=p.platform_id AND tr.branch_id=b.branch_id AND b.product_id=pr.product_id AND tr.result_status_id=trsl.result_status_id AND tcsg.testcase_id=tr.testcase_id AND tcsg.subgroup_id=sg.subgroup_id AND sg.subgroup_id=sgtg.subgroup_id AND sgtg.testgroup_id=tg.testgroup_id AND tr.user_id=u.user_id AND tr.valid=1';
|
||||
|
||||
my $limit = 'LIMIT ';
|
||||
|
||||
foreach my $criterion (@$where_criteria) {
|
||||
$criterion->{'value'} =~ s/'/\'/g;
|
||||
if ($criterion->{'field'} eq 'branch') {
|
||||
$where .= " AND b.name='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'locale') {
|
||||
$where .= " AND tr.locale_abbrev='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'product') {
|
||||
$where .= " AND pr.name='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'platform') {
|
||||
$where .= " AND p.name='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'test_group') {
|
||||
$where .= " AND tg.name='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'testcase_id') {
|
||||
$where .= " AND tr.testcase_id='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'summary') {
|
||||
$where .= ' AND t.summary LIKE \'%%' . $criterion->{'value'} . '%%\'';
|
||||
} elsif ($criterion->{'field'} eq 'email') {
|
||||
$where .= ' AND u.email LIKE \'%%' . $criterion->{'value'} . '%%\'';
|
||||
} elsif ($criterion->{'field'} eq 'result_status') {
|
||||
$where .= " AND trsl.class_name='" . $criterion->{'value'} . "'";
|
||||
} elsif ($criterion->{'field'} eq 'trusted_only') {
|
||||
if ($from !~ /users u/) {
|
||||
$from .= ", users u";
|
||||
}
|
||||
$where .= " AND u.user_id=tr.user_id AND u.is_admin=1";
|
||||
} elsif ($criterion->{'field'} eq 'start_date') {
|
||||
my $start_timestamp = &Date::Manip::UnixDate(&Date::Manip::ParseDateString($criterion->{'value'}),"%q");
|
||||
if ($start_timestamp !~ /^\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/) {
|
||||
print STDERR "Unable to parse a valid start date from '$criterion->{'value'},' ignoring.\n";
|
||||
} else {
|
||||
$where .= " AND tr.submission_time>=$start_timestamp";
|
||||
}
|
||||
} elsif ($criterion->{'field'} eq 'end_date') {
|
||||
my $end_timestamp = &Date::Manip::UnixDate(&Date::Manip::ParseDateString($criterion->{'value'}),"%q");
|
||||
if ($end_timestamp !~ /^\d\d\d\d\d\d\d\d\d\d\d\d\d\d$/) {
|
||||
print STDERR "Unable to parse a valid end date from '$criterion->{'value'},' ignoring.\n";
|
||||
} else {
|
||||
$where .= " AND tr.submission_time<=$end_timestamp";
|
||||
}
|
||||
} elsif ($criterion->{'field'} eq 'timespan') {
|
||||
next if ($criterion->{'value'} eq 'all');
|
||||
my $day_delta = $criterion->{'value'};
|
||||
my $err;
|
||||
my $timestamp =
|
||||
&Date::Manip::UnixDate(&Date::Manip::DateCalc("now",
|
||||
"$day_delta days",
|
||||
\$err),
|
||||
"%q");
|
||||
$where .= " AND tr.submission_time>=$timestamp";
|
||||
|
||||
} elsif ($criterion->{'field'} eq 'search_field') {
|
||||
($from,$where) = &_processSearchField($criterion,$from,$where);
|
||||
} else {
|
||||
# Skip unknown field
|
||||
}
|
||||
}
|
||||
|
||||
my $order_by = 'ORDER BY ';
|
||||
foreach my $criterion (@$order_by_criteria) {
|
||||
# Skip empty fields.
|
||||
next if (!$criterion or !$criterion->{'field'});
|
||||
|
||||
if ($criterion->{'field'} eq 'created') {
|
||||
$order_by .= "tr.submission_time $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'product') {
|
||||
$order_by .= "pr.name $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'platform') {
|
||||
$order_by .= "p.name $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'test_group') {
|
||||
$order_by .= "tg.name $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'testcase_id') {
|
||||
$order_by .= "tr.testcase_id $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'summary') {
|
||||
$order_by .= "t.summary $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'result_status') {
|
||||
$order_by .= "trsl.class_name $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'branch') {
|
||||
$order_by .= "b.name $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'locale') {
|
||||
$order_by .= "tr.locale_abbrev $criterion->{'direction'},";
|
||||
} elsif ($criterion->{'field'} eq 'email') {
|
||||
$order_by .= "u.email $criterion->{'direction'},";
|
||||
} else {
|
||||
# Skip unknown field
|
||||
}
|
||||
}
|
||||
if ($order_by eq 'ORDER BY ') {
|
||||
$order_by .= 'tr.submission_time DESC';
|
||||
} else {
|
||||
chop($order_by);
|
||||
}
|
||||
|
||||
if ($limit_value and $limit_value ne '') {
|
||||
$limit .= "$limit_value";
|
||||
} else {
|
||||
$limit .= "$_num_results_default";
|
||||
}
|
||||
|
||||
my $sql = "$select $from $where $order_by $limit";
|
||||
#print $sql,"<br/>\n";
|
||||
Litmus::DB::Testresult->set_sql(TestResults => qq{
|
||||
$sql
|
||||
});
|
||||
|
||||
my @rows = $self->search_TestResults();
|
||||
|
||||
return \@rows;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# &_processSearchField(\%\$\$)
|
||||
#
|
||||
#########################################################################
|
||||
sub _processSearchField(\%) {
|
||||
my ($search_field,$from,$where) = @_;
|
||||
|
||||
my $table_field = "";
|
||||
if ($search_field->{'search_field'} eq 'build_id') {
|
||||
$table_field='tr.build_id';
|
||||
} elsif ($search_field->{'search_field'} eq 'comments') {
|
||||
$table_field='c.comment';
|
||||
} elsif ($search_field->{'search_field'} eq 'locale') {
|
||||
$table_field='tr.locale_abbrev';
|
||||
} elsif ($search_field->{'search_field'} eq 'opsys') {
|
||||
$table_field='o.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'platform') {
|
||||
$table_field='p.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'product') {
|
||||
$table_field='pr.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'result_status') {
|
||||
$table_field='trsl.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'subgroup') {
|
||||
$table_field='sg.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'email') {
|
||||
if ($from !~ /users u/) {
|
||||
$from .= ", users u";
|
||||
$where .= " AND tr.user_id=u.user_id";
|
||||
}
|
||||
$table_field='u.email';
|
||||
} elsif ($search_field->{'search_field'} eq 'summary') {
|
||||
$table_field='t.summary';
|
||||
} elsif ($search_field->{'search_field'} eq 'test_group') {
|
||||
$table_field='tg.name';
|
||||
} elsif ($search_field->{'search_field'} eq 'user_agent') {
|
||||
$table_field='tr.user_agent';
|
||||
} else {
|
||||
return ($from,$where);
|
||||
}
|
||||
|
||||
if ($search_field->{'match_criteria'} eq 'contains_all' or
|
||||
$search_field->{'match_criteria'} eq 'contains_any' or
|
||||
$search_field->{'match_criteria'} eq 'not_contain_any') {
|
||||
|
||||
my $join = "";
|
||||
if ($search_field->{'match_criteria'} eq 'contains_all') {
|
||||
$join = 'AND';
|
||||
} else {
|
||||
$join = 'OR';
|
||||
}
|
||||
|
||||
my @words = split(/ /,$search_field->{'value'});
|
||||
if ($search_field->{'match_criteria'} eq 'not_contain_any') {
|
||||
$where .= " AND NOT (";
|
||||
} else {
|
||||
$where .= " AND (";
|
||||
}
|
||||
my $first_pass = 1;
|
||||
foreach my $word (@words) {
|
||||
if ( $first_pass ) {
|
||||
$where .= "UPPER($table_field) LIKE UPPER('%%" . $word . "%%')";
|
||||
$first_pass = 0;
|
||||
} else {
|
||||
$where .= " $join UPPER($table_field) LIKE UPPER('%%" . $word . "%%')";
|
||||
}
|
||||
}
|
||||
$where .= ")";
|
||||
} elsif ($search_field->{'match_criteria'} eq 'contains') {
|
||||
$where .= " AND UPPER($table_field) LIKE UPPER('%%" . $search_field->{'value'} . "%%')";
|
||||
} elsif ($search_field->{'match_criteria'} eq 'contains_case') {
|
||||
$where .= " AND $table_field LIKE '%%" . $search_field->{'value'} . "%%'";
|
||||
} elsif ($search_field->{'match_criteria'} eq 'not_contain') {
|
||||
$where .= " AND UPPER($table_field) NOT LIKE UPPER('%%" . $search_field->{'value'} . "%%')";
|
||||
} elsif ($search_field->{'match_criteria'} eq 'regexp') {
|
||||
$where .= " AND $table_field REGEXP '" . $search_field->{'value'} . "'";
|
||||
} elsif ($search_field->{'match_criteria'} eq 'not_regexp') {
|
||||
$where .= " AND $table_field NOT REGEXP '" . $search_field->{'value'} . "'";
|
||||
} else {
|
||||
# Ignore unknown match criteria.
|
||||
return ($from,$where);
|
||||
}
|
||||
|
||||
return ($from,$where);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
#########################################################################
|
||||
sub getCommonResults($$) {
|
||||
my ($self,$status,$limit_value) = @_;
|
||||
|
||||
if (!$status) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
if (!$limit_value) {
|
||||
$limit_value = $_num_results_default;
|
||||
}
|
||||
|
||||
my @rows = $self->search_CommonResults($status);
|
||||
return \@rows;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,119 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DB::User;
|
||||
|
||||
use strict;
|
||||
use Litmus::Config;
|
||||
use base 'Litmus::DBI';
|
||||
|
||||
Litmus::DB::User->table('users');
|
||||
|
||||
Litmus::DB::User->columns(All => qw/user_id bugzilla_uid email password realname irc_nickname enabled is_admin authtoken/);
|
||||
|
||||
Litmus::DB::User->column_alias("is_trusted", "istrusted");
|
||||
Litmus::DB::User->column_alias("is_admin", "is_trusted");
|
||||
|
||||
Litmus::DB::User->has_many(test_results => "Litmus::DB::Testresult");
|
||||
Litmus::DB::User->has_many(sessions => "Litmus::DB::Session");
|
||||
|
||||
# ZLL: only load BugzillaUser if Bugzilla Auth is actually enabled
|
||||
if ($Litmus::Config::bugzilla_auth_enabled) {
|
||||
Litmus::DB::User->has_a(bugzilla_uid => "Litmus::DB::BugzillaUser");
|
||||
}
|
||||
|
||||
__PACKAGE__->set_sql(RetrieveAll => qq{
|
||||
SELECT __ESSENTIAL__
|
||||
FROM __TABLE__
|
||||
ORDER BY email ASC
|
||||
});
|
||||
|
||||
__PACKAGE__->set_sql(TopTesters => qq{
|
||||
SELECT users.user_id, users.email, count(*) AS num_results
|
||||
FROM users, test_results
|
||||
WHERE users.user_id=test_results.user_id
|
||||
GROUP BY user_id
|
||||
ORDER BY num_results DESC
|
||||
LIMIT 15
|
||||
});
|
||||
|
||||
# the COLLATE latin1_general_ci sillyness forces a case-insensitive match
|
||||
# removed a LIMIT 300 to work around a mysql bug in the ancient version
|
||||
# on rodan
|
||||
__PACKAGE__->set_sql(FullTextMatches => q{
|
||||
SELECT *
|
||||
FROM __TABLE__
|
||||
WHERE
|
||||
email COLLATE latin1_general_ci like concat('%%',?,'%%') OR
|
||||
irc_nickname COLLATE latin1_general_ci like concat('%%',?,'%%') OR
|
||||
realname COLLATE latin1_general_ci like concat('%%',?,'%%')
|
||||
ORDER BY email ASC
|
||||
});
|
||||
|
||||
|
||||
|
||||
#########################################################################
|
||||
# returns the crypt'd password from a linked Bugzilla account if it
|
||||
# exists or the Litmus user account
|
||||
sub getRealPasswd {
|
||||
my $self = shift;
|
||||
if ($self->bugzilla_uid()) {
|
||||
return $self->bugzilla_uid()->cryptpassword();
|
||||
} else {
|
||||
return $self->password();
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getDisplayName() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->irc_nickname if ($self->irc_nickname and
|
||||
$self->irc_nickname ne '');
|
||||
return $self->realname if ($self->realname and
|
||||
$self->realname ne '');
|
||||
|
||||
if ($self->email and
|
||||
$self->email ne '') {
|
||||
my $display_name = $self->email;
|
||||
$display_name =~ s/\@.*$//g;
|
||||
return $display_name
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::DBI;
|
||||
|
||||
require Apache::DBI;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Litmus::Config;
|
||||
use Memoize;
|
||||
|
||||
use base 'Class::DBI::mysql';
|
||||
|
||||
our $dsn = "dbi:mysql:database=$Litmus::Config::db_name;host=$Litmus::Config::db_host;port=3306";
|
||||
|
||||
|
||||
|
||||
our %column_aliases;
|
||||
|
||||
Litmus::DBI->connection( $dsn,
|
||||
$Litmus::Config::db_user,
|
||||
$Litmus::Config::db_pass,
|
||||
{AutoCommit=>1}
|
||||
);
|
||||
Litmus::DBI->autoupdate(1);
|
||||
|
||||
# In some cases, we have column names that make sense from a database perspective
|
||||
# (i.e. subgroup_id), but that don't make sense from a class/object perspective
|
||||
# (where subgroup would be more appropriate). To handle this, we allow for
|
||||
# Litmus::DBI's subclasses to set column aliases with the column_alias() sub.
|
||||
# Takes the database column name and the alias name.
|
||||
sub column_alias {
|
||||
my ($self, $db_name, $alias_name) = @_;
|
||||
|
||||
$column_aliases{$alias_name} = $db_name;
|
||||
}
|
||||
|
||||
# here's where the actual work happens. We consult our alias list
|
||||
# (as created by calls to column_alias()) and substitute the
|
||||
# database column if we find a match
|
||||
memoize('find_column');
|
||||
sub find_column {
|
||||
my $self = shift;
|
||||
my $wanted = shift;
|
||||
|
||||
if (ref $self) {
|
||||
$wanted =~ s/^.*::(\w+)$/$1/;
|
||||
}
|
||||
if ($column_aliases{$wanted}) {
|
||||
return $column_aliases{$wanted};
|
||||
} else {
|
||||
# not an alias, so we use the normal
|
||||
# find_column() from Class::DBI
|
||||
$self->SUPER::find_column($wanted);
|
||||
}
|
||||
}
|
||||
|
||||
sub AUTOLOAD {
|
||||
my $self = shift;
|
||||
my @args = @_;
|
||||
my $name = our $AUTOLOAD;
|
||||
|
||||
my $col = $self->find_column($name);
|
||||
if (!$col) {
|
||||
internalError("tried to call Litmus::DBI method $name which does not exist");
|
||||
}
|
||||
|
||||
return $self->$col(@args);
|
||||
}
|
||||
|
||||
sub _croak {
|
||||
my ($self, $message, %info) = @_;
|
||||
internalError($message);
|
||||
return;
|
||||
}
|
||||
|
||||
# Get Class::DBI's default dbh options
|
||||
my $db_options = { __PACKAGE__->_default_attributes };
|
||||
|
||||
__PACKAGE__->_remember_handle('Main'); # so dbi_commit works
|
||||
|
||||
# override default to avoid using Ima::DBI closure
|
||||
sub db_Main {
|
||||
my $dbh;
|
||||
if ( $ENV{'MOD_PERL'} and !$Apache::ServerStarting ) {
|
||||
$dbh = Apache->request()->pnotes('dbh');
|
||||
}
|
||||
if ( !$dbh ) {
|
||||
$dbh = DBI->connect(
|
||||
$dsn, $Litmus::Config::db_user,
|
||||
$Litmus::Config::db_pass, $db_options
|
||||
);
|
||||
if ( $ENV{'MOD_PERL'} and !$Apache::ServerStarting ) {
|
||||
Apache->request()->pnotes( 'dbh', $dbh );
|
||||
}
|
||||
}
|
||||
return $dbh;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
@@ -1,275 +0,0 @@
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
|
||||
# Utility functions (based on those from Bugzilla's checksetup.pl to
|
||||
# create new fields and indicies to the schema when upgrading an installation.
|
||||
|
||||
package Litmus::DBTools;
|
||||
|
||||
use strict;
|
||||
|
||||
#########################################################################
|
||||
sub new {
|
||||
my ($class, $dbh) = @_;
|
||||
my $self = {};
|
||||
$self->{'dbh'} = $dbh;
|
||||
bless($self);
|
||||
return $self;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub ChangeFieldType {
|
||||
my ($self, $table, $field, $newtype) = @_;
|
||||
|
||||
my $ref = $self->GetFieldDef($table, $field);
|
||||
#print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n";
|
||||
|
||||
my $oldtype = $ref->[1];
|
||||
if (! $ref->[2]) {
|
||||
$oldtype .= qq{ not null};
|
||||
}
|
||||
if ($ref->[4]) {
|
||||
$oldtype .= qq{ default "$ref->[4]"};
|
||||
}
|
||||
|
||||
if ($oldtype ne $newtype) {
|
||||
print "Updating field type $field in table $table ...\n";
|
||||
print "old: $oldtype\n";
|
||||
print "new: $newtype\n";
|
||||
# 'not null' should be passed as part of the call to ChangeFieldType()
|
||||
# $newtype .= " NOT NULL" if $$ref[3];
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
CHANGE $field
|
||||
$field $newtype");
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub RenameTable {
|
||||
my ($self, $table, $newname) = @_;
|
||||
|
||||
my $ref = $self->TableExists($table);
|
||||
return unless $ref; # already renamed?
|
||||
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
RENAME TO $newname");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub RenameField {
|
||||
my ($self, $table, $field, $newname) = @_;
|
||||
|
||||
my $ref = $self->GetFieldDef($table, $field);
|
||||
return unless $ref; # already fixed?
|
||||
#print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n";
|
||||
|
||||
if ($$ref[1] ne $newname) {
|
||||
print "Updating field $field in table $table ...\n";
|
||||
my $type = $$ref[1];
|
||||
$type .= " NOT NULL" if !$$ref[2];
|
||||
$type .= " auto_increment" if $$ref[5] =~ /auto_increment/;
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
CHANGE $field
|
||||
$newname $type");
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub AddField {
|
||||
my ($self, $table, $field, $definition) = @_;
|
||||
|
||||
my $ref = $self->GetFieldDef($table, $field);
|
||||
return if $ref; # already added?
|
||||
|
||||
print "Adding new field $field to table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
ADD COLUMN $field $definition");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub AddKey {
|
||||
my ($self, $table, $field, $definition) = @_;
|
||||
|
||||
my $ref = $self->GetIndexDef($table, $field);
|
||||
return if $ref; # already added?
|
||||
|
||||
print "Adding new key $field to table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
ADD KEY $field $definition");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub AddUniqueKey {
|
||||
my ($self, $table, $field, $definition) = @_;
|
||||
|
||||
my $ref = $self->GetIndexDef($table, $field);
|
||||
return if $ref; # already added?
|
||||
|
||||
print "Adding new key $field to table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
ADD UNIQUE KEY $field $definition");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub AddFullText {
|
||||
my ($self, $table, $index, $definition) = @_;
|
||||
|
||||
my $ref = $self->GetIndexDef($table, $index);
|
||||
return if $ref; # already added?
|
||||
|
||||
print "Adding new fulltext $index to table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
ADD FULLTEXT $index $definition");
|
||||
}
|
||||
|
||||
|
||||
#########################################################################
|
||||
sub DropField {
|
||||
my ($self, $table, $field) = @_;
|
||||
|
||||
my $ref = $self->GetFieldDef($table, $field);
|
||||
return unless $ref; # already dropped?
|
||||
|
||||
print "Deleting unused field $field from table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
DROP COLUMN $field");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub DropTable {
|
||||
my ($self, $table, $field) = @_;
|
||||
|
||||
my $ref = $self->TableExists($table);
|
||||
return unless $ref; # already dropped?
|
||||
|
||||
print "Deleting unused table $table ...\n";
|
||||
$self->{'dbh'}->do("DROP TABLE $table");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
# this uses a mysql specific command.
|
||||
sub TableExists {
|
||||
my ($self, $table) = @_;
|
||||
my @tables;
|
||||
my $dbtable;
|
||||
my $exists = 0;
|
||||
my $sth = $self->{'dbh'}->prepare("SHOW TABLES");
|
||||
$sth->execute;
|
||||
while ( ($dbtable) = $sth->fetchrow_array ) {
|
||||
if ($dbtable eq $table) {
|
||||
$exists = 1;
|
||||
}
|
||||
}
|
||||
$sth->finish;
|
||||
return $exists;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub GetFieldDef {
|
||||
my ($self, $table, $field) = @_;
|
||||
my $sth = $self->{'dbh'}->prepare("SHOW COLUMNS FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
while (my $ref = $sth->fetchrow_arrayref) {
|
||||
next if $$ref[0] ne $field;
|
||||
$sth->finish;
|
||||
return $ref;
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub GetIndexDef {
|
||||
my ($self, $table, $field) = @_;
|
||||
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
while (my $ref = $sth->fetchrow_arrayref) {
|
||||
next if $$ref[2] ne $field;
|
||||
$sth->finish;
|
||||
return $ref;
|
||||
}
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub CountIndexes {
|
||||
my ($self, $table) = @_;
|
||||
|
||||
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
if ( $sth->rows == -1 ) {
|
||||
$sth->finish;
|
||||
die ("Unexpected response while counting indexes in $table:" .
|
||||
" \$sth->rows == -1");
|
||||
}
|
||||
|
||||
my $rows = $sth->rows;
|
||||
$sth->finish;
|
||||
return ($rows);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub DropIndex {
|
||||
my ($self, $table, $index) = @_;
|
||||
|
||||
my $ref = $self->GetIndexDef($table, $index);
|
||||
return unless $ref; # no matching index?
|
||||
|
||||
print "Removing index $index from table $table ...\n";
|
||||
$self->{'dbh'}->do("ALTER TABLE $table
|
||||
DROP INDEX $index");
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub DropIndexes {
|
||||
my ($self, $table) = @_;
|
||||
my %SEEN;
|
||||
|
||||
# get the list of indexes
|
||||
#
|
||||
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
|
||||
$sth->execute;
|
||||
|
||||
# drop each index
|
||||
#
|
||||
while ( my $ref = $sth->fetchrow_arrayref) {
|
||||
|
||||
# note that some indexes are described by multiple rows in the
|
||||
# index table, so we may have already dropped the index described
|
||||
# in the current row.
|
||||
#
|
||||
next if exists $SEEN{$$ref[2]};
|
||||
|
||||
if ($$ref[2] eq 'PRIMARY') {
|
||||
# The syntax for dropping a PRIMARY KEY is different
|
||||
# from the normal DROP INDEX syntax.
|
||||
$self->{'dbh'}->do("ALTER TABLE $table DROP PRIMARY KEY");
|
||||
}
|
||||
else {
|
||||
$self->{'dbh'}->do("ALTER TABLE $table DROP INDEX $$ref[2]");
|
||||
}
|
||||
$SEEN{$$ref[2]} = 1;
|
||||
|
||||
}
|
||||
$sth->finish;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,84 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
# General error reporting
|
||||
|
||||
package Litmus::Error;
|
||||
|
||||
use strict;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
@Litmus::Error::EXPORT = qw(
|
||||
basicError
|
||||
invalidInputError
|
||||
internalError
|
||||
lastDitchError
|
||||
);
|
||||
|
||||
# just a general run of the mill error
|
||||
sub basicError {
|
||||
my $message = shift;
|
||||
_doError($message);
|
||||
exit;
|
||||
}
|
||||
|
||||
# used to alert the user when an unexpected input value is found
|
||||
sub invalidInputError($) {
|
||||
my $message = shift;
|
||||
_doError("Invalid Input - $message");
|
||||
exit;
|
||||
}
|
||||
|
||||
sub internalError($) {
|
||||
my $message = shift;
|
||||
_doError("Litmus has suffered an internal error - $message");
|
||||
exit;
|
||||
}
|
||||
|
||||
# an error type that does not use a template error message. Used if we
|
||||
# can't even process the error template.
|
||||
sub lastDitchError($) {
|
||||
my $message = shift;
|
||||
print "Error - Litmus has suffered a serious fatal internal error - $message";
|
||||
exit;
|
||||
}
|
||||
|
||||
sub _doError($) {
|
||||
my $message = shift;
|
||||
my $vars = {
|
||||
message => $message,
|
||||
};
|
||||
Litmus->template()->process("error/error.html.tmpl", $vars) ||
|
||||
lastDitchError(Litmus->template()->error());
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,342 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::FormWidget;
|
||||
use strict;
|
||||
|
||||
BEGIN {
|
||||
use Exporter ();
|
||||
use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
|
||||
$VERSION = 0.01;
|
||||
@ISA = qw (Exporter);
|
||||
#Give a hoot don't pollute, do not export more than needed by default
|
||||
@EXPORT = qw ( getProducts );
|
||||
@EXPORT_OK = qw ();
|
||||
%EXPORT_TAGS = ();
|
||||
}
|
||||
|
||||
use DBI;
|
||||
use Litmus::DBI;
|
||||
|
||||
our $_dbh = Litmus::DBI->db_Main();
|
||||
|
||||
#########################################################################
|
||||
=head1 NAME
|
||||
|
||||
Litmus::FormWidget - Create value lists to be used in HTML forms
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Litmus::FormWidget
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Litmus::FormWidget creates value lists to be used in HTML forms.
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
|
||||
|
||||
=head1 SUPPORT
|
||||
|
||||
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Chris Cooper
|
||||
CPAN ID: CCOOPER
|
||||
Mozilla Corporation
|
||||
ccooper@deadsquid.com
|
||||
http://litmus.mozilla.org/
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
perl(1).
|
||||
|
||||
=cut
|
||||
|
||||
#########################################################################
|
||||
|
||||
#sub new
|
||||
#{
|
||||
# my ($class, %parameters) = @_;
|
||||
# my $self = bless ({}, ref ($class) || $class);
|
||||
# return ($self);
|
||||
#}
|
||||
|
||||
#########################################################################
|
||||
|
||||
=head2 getProducts
|
||||
|
||||
Usage : How to use this function/method
|
||||
Purpose : What it does
|
||||
Returns : What it returns
|
||||
Argument : What it wants to know
|
||||
Throws : Exceptions and other anomolies
|
||||
Comments : This is a sample subroutine header.
|
||||
: It is polite to include more pod and fewer comments.
|
||||
|
||||
See Also :
|
||||
|
||||
=cut
|
||||
|
||||
#########################################################################
|
||||
sub getProducts()
|
||||
{
|
||||
my $sql = "SELECT name, product_id FROM products ORDER BY name";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getUniquePlatforms()
|
||||
{
|
||||
my $sql = "SELECT DISTINCT(name), platform_id FROM platforms ORDER BY name";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getPlatforms()
|
||||
{
|
||||
my $sql = "SELECT platform_id, name from platforms ORDER BY name ASC";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getBranches()
|
||||
{
|
||||
my $sql = "SELECT name, branch_id FROM branches ORDER BY name ASC";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getUniqueBranches()
|
||||
{
|
||||
my $sql = "SELECT DISTINCT(name) FROM branches ORDER BY name ASC";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getOpsyses()
|
||||
{
|
||||
my $sql = "SELECT name, opsys_id FROM opsyses ORDER BY name ASC";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getUniqueOpsyses()
|
||||
{
|
||||
my $sql = "SELECT DISTINCT(name) FROM opsyses ORDER BY name ASC";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getLogTypes()
|
||||
{
|
||||
my $sql = "SELECT DISTINCT(name) FROM log_type_lookup ORDER BY name";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getTestStatuses()
|
||||
{
|
||||
my @TestStatuses = ({name => 'Enabled'},
|
||||
{name => 'Disabled'});
|
||||
return \@TestStatuses;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getResultStatuses()
|
||||
{
|
||||
my $sql = "SELECT result_status_id,class_name FROM test_result_status_lookup ORDER BY result_status_id";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getTestcaseIDs()
|
||||
{
|
||||
my $sql = "SELECT testcase_id FROM testcases ORDER BY testcase_id";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getTestcases()
|
||||
{
|
||||
my $sql = "SELECT testcase_id, summary, product_id FROM testcases ORDER BY testcase_id";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getSubgroups()
|
||||
{
|
||||
my $sql = "SELECT subgroup_id, name, product_id FROM subgroups ORDER BY name, subgroup_id";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getTestgroups()
|
||||
{
|
||||
my $sql = "SELECT testgroup_id, name, product_id FROM testgroups ORDER BY name, testgroup_id";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
|
||||
#########################################################################
|
||||
sub getLocales()
|
||||
{
|
||||
my @locales = Litmus::DB::Locale->retrieve_all();
|
||||
return \@locales;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getUsers()
|
||||
{
|
||||
my @users = Litmus::DB::User->retrieve_all();
|
||||
return \@users;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getAuthors()
|
||||
{
|
||||
my $sql = "SELECT user_id, email FROM users WHERE is_admin=1 ORDER BY email";
|
||||
return _getValues($sql);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#########################################################################
|
||||
sub getFields()
|
||||
{
|
||||
my @fields = (
|
||||
{ name => 'build_id',
|
||||
display_string => "Build ID", },
|
||||
{ name => 'comment',
|
||||
display_string => "Comments", },
|
||||
{ name => 'locale',
|
||||
display_string => "Locale", },
|
||||
{ name => 'opsys',
|
||||
display_string => "Operating System", },
|
||||
{ name => 'platform',
|
||||
display_string => "Platform", },
|
||||
{ name => 'product',
|
||||
display_string => "Product", },
|
||||
{ name => 'result_status',
|
||||
display_string => "Result Status", },
|
||||
{ name => 'subgroup',
|
||||
display_string => "Subgroup", },
|
||||
{ name => 'email',
|
||||
display_string => "Submitted By", },
|
||||
{ name => 'summary',
|
||||
display_string => "Summary", },
|
||||
{ name => 'testgroup',
|
||||
display_string => "Testgroup", },
|
||||
{ name => 'user_agent',
|
||||
display_string => "User Agent", },
|
||||
);
|
||||
return \@fields;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getMatchCriteria()
|
||||
{
|
||||
my @match_criteria = (
|
||||
{ name => "contains_all",
|
||||
display_string => "contains all of the words/strings" },
|
||||
{ name => "contains_any",
|
||||
display_string => "contains any of the words/strings" },
|
||||
{ name => "contains",
|
||||
display_string => "contains the word/string" },
|
||||
{ name => "contains_case",
|
||||
display_string => "contains the word/string (exact case)" },
|
||||
{ name => "not_contain",
|
||||
display_string => "does not contains the word/string" },
|
||||
{ name => "not_contain_any",
|
||||
display_string => "does not contains any of the words/string" },
|
||||
{ name => "regexp",
|
||||
display_string => "matches the regexp" },
|
||||
{ name => "not_regexp",
|
||||
display_string => "does not match the regexp" },
|
||||
);
|
||||
return \@match_criteria;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub getSortFields()
|
||||
{
|
||||
my @sort_fields = (
|
||||
{ name => "branch",
|
||||
display_string => "Branch"},
|
||||
{ name => "created",
|
||||
display_string => "Date"},
|
||||
{ name => "locale",
|
||||
display_string => "Locale"},
|
||||
{ name => "platform",
|
||||
display_string => "Platform"},
|
||||
{ name => "product",
|
||||
display_string => "Product"},
|
||||
{ name => "email",
|
||||
display_string => "Submitted By"},
|
||||
{ name => "summary",
|
||||
display_string => "Summary"},
|
||||
{ name => "result_status",
|
||||
display_string => "Status"},
|
||||
{ name => "testcase_id",
|
||||
display_string => "Testcase ID#"},
|
||||
{ name => "testgroup",
|
||||
display_string => "Testgroup"},
|
||||
);
|
||||
return \@sort_fields;
|
||||
}
|
||||
|
||||
#########################################################################
|
||||
sub _getValues($)
|
||||
{
|
||||
my ($sql) = @_;
|
||||
my $sth = $_dbh->prepare_cached($sql);
|
||||
$sth->execute();
|
||||
my @rows;
|
||||
while (my $data = $sth->fetchrow_hashref) {
|
||||
push @rows, $data;
|
||||
}
|
||||
return \@rows;
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::StripScripts;
|
||||
use base qw(HTML::StripScripts::Parser);
|
||||
|
||||
# Override broken href validation code.
|
||||
sub validate_href_attribute {
|
||||
my ($self, $text) = @_;
|
||||
|
||||
$self->SUPER::validate_href_attribute or $text =~ m<^((https?|ftp|mailto)://[\w\-\.]{1,100}(?:\:\d{1,5})?(?:/(?:[\w\-.!~*|;:/?=+\$\,%#]|&){0,100})?)$>x ? $1 : undef;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,263 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::SysConfig;
|
||||
|
||||
use strict;
|
||||
|
||||
require Exporter;
|
||||
use Litmus;
|
||||
use Litmus::DB::Locale;
|
||||
use Litmus::DB::Product;
|
||||
use Litmus::Error;
|
||||
use Litmus::Utils;
|
||||
use CGI;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw();
|
||||
|
||||
my $configcookiename = $Litmus::Config::sysconfig_cookiename;
|
||||
|
||||
sub new {
|
||||
my ($class, $product, $platform, $opsys, $branch, $build_id, $locale) = @_;
|
||||
|
||||
my $self = {};
|
||||
bless($self);
|
||||
|
||||
$self->{"product"} = $product;
|
||||
$self->{"platform"} = $platform;
|
||||
$self->{"opsys"} = $opsys;
|
||||
$self->{"branch"} = $branch;
|
||||
$self->{"build_id"} = $build_id;
|
||||
$self->{"locale"} = $locale;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub setCookie {
|
||||
my $self = shift;
|
||||
|
||||
my %cookiedata;
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
my $cookie = $c->cookie(
|
||||
-name => $configcookiename.'_'.$self->{"product"}->product_id(),
|
||||
-value => join('|', $self->{"product"}->product_id(), $self->{"platform"}->platform_id(),
|
||||
$self->{"opsys"}->opsys_id(), $self->{"branch"}->branch_id(), $self->{"build_id"}, $self->{"locale"}->abbrev()),
|
||||
-domain => $main::ENV{"HTTP_HOST"},
|
||||
);
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
|
||||
sub getCookie {
|
||||
my $self = shift;
|
||||
my $c = Litmus->cgi();
|
||||
my $product = shift;
|
||||
|
||||
return Litmus::SysConfig->
|
||||
realGetCookie($configcookiename.'_'.$product->product_id());
|
||||
|
||||
}
|
||||
|
||||
# returns sysconfig objects corresponding to all sysconfig cookies the user
|
||||
# has set
|
||||
sub getAllCookies {
|
||||
my $self = shift;
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my @cookies = ();
|
||||
my @names = $c->cookie();
|
||||
foreach my $cur (@names) {
|
||||
if ($cur =~ /^\Q$configcookiename\E_/) {
|
||||
push(@cookies, Litmus::SysConfig->realGetCookie($cur));
|
||||
}
|
||||
}
|
||||
if (@cookies == 0) { push(@cookies, undef) }
|
||||
return @cookies;
|
||||
}
|
||||
|
||||
sub realGetCookie {
|
||||
my $self = shift;
|
||||
my $cookiename = shift;
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
my $cookie = $c->cookie($cookiename);
|
||||
if (! $cookie) {
|
||||
return;
|
||||
}
|
||||
|
||||
my @sysconfig = split(/\|/, $cookie);
|
||||
|
||||
return new(undef,
|
||||
Litmus::DB::Product->retrieve($sysconfig[0]),
|
||||
Litmus::DB::Platform->retrieve($sysconfig[1]),
|
||||
Litmus::DB::Opsys->retrieve($sysconfig[2]),
|
||||
Litmus::DB::Branch->retrieve($sysconfig[3]),
|
||||
$sysconfig[4],
|
||||
Litmus::DB::Locale->retrieve($sysconfig[5])
|
||||
);
|
||||
}
|
||||
|
||||
sub product() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"product"};
|
||||
}
|
||||
|
||||
sub platform() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"platform"};
|
||||
}
|
||||
|
||||
sub opsys() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"opsys"};
|
||||
}
|
||||
|
||||
sub branch() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"branch"};
|
||||
}
|
||||
|
||||
sub build_id() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"build_id"};
|
||||
}
|
||||
|
||||
sub locale() {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{"locale"};
|
||||
}
|
||||
|
||||
|
||||
# display the system configuration form
|
||||
# optionally takes the product to configure for
|
||||
# and requires a url to load when done. this
|
||||
# url should call processForm() to
|
||||
# set the sysconfig cookie and do
|
||||
# error handling.
|
||||
# optionaly, pass a CGI object and its
|
||||
# data will be stored in the form so
|
||||
# the receiving script can access it.
|
||||
sub displayForm {
|
||||
my $self = shift;
|
||||
my $product = shift;
|
||||
my $goto = shift;
|
||||
my $c = shift;
|
||||
|
||||
my @products;
|
||||
# if we already know the product, then just send it on:
|
||||
if ($product) {
|
||||
$products[0] = $product;
|
||||
} else {
|
||||
# we need to ask the user for the product then
|
||||
@products = Litmus::DB::Product->retrieve_all();
|
||||
}
|
||||
|
||||
my @locales = Litmus::DB::Locale->retrieve_all(
|
||||
{ order_by => 'abbrev' }
|
||||
);
|
||||
my $vars = {
|
||||
locales => \@locales,
|
||||
products => \@products,
|
||||
ua => Litmus::UserAgentDetect->new(),
|
||||
"goto" => $goto,
|
||||
cgidata => $c,
|
||||
};
|
||||
|
||||
# if the user already has a cookie set for this product, then
|
||||
# load those values as defaults:
|
||||
if ($product && Litmus::SysConfig->getCookie($product)) {
|
||||
my $sysconfig = Litmus::SysConfig->getCookie($product);
|
||||
$vars->{"defaultplatform"} = $sysconfig->platform();
|
||||
$vars->{"defaultopsys"} = $sysconfig->opsys();
|
||||
$vars->{"defaultbranch"} = $sysconfig->branch();
|
||||
$vars->{"defaultlocale"} = $sysconfig->locale();
|
||||
}
|
||||
|
||||
# send a default build id if we have one:
|
||||
my @cookies = Litmus::SysConfig->getAllCookies();
|
||||
if ($cookies[0]) { $vars->{"defaultbuildid"} = $cookies[0]->build_id() }
|
||||
|
||||
my $cookie = Litmus::Auth::getCurrentUser();
|
||||
$vars->{"defaultemail"} = $cookie;
|
||||
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);
|
||||
|
||||
$vars->{"title"} = "Run Tests";
|
||||
|
||||
Litmus->template()->process("runtests/sysconfig.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
}
|
||||
|
||||
# process a form containing sysconfig information.
|
||||
# takes a CGI object containing param data
|
||||
sub processForm {
|
||||
my $self = shift;
|
||||
my $c = shift;
|
||||
|
||||
my $product = Litmus::DB::Product->retrieve($c->param("product"));
|
||||
my $platform = Litmus::DB::Platform->retrieve($c->param("platform"));
|
||||
my $opsys = Litmus::DB::Opsys->retrieve($c->param("opsys"));
|
||||
my $branch = Litmus::DB::Branch->retrieve($c->param("branch"));
|
||||
my $build_id = $c->param("build_id");
|
||||
my $locale = Litmus::DB::Locale->retrieve($c->param("locale"));
|
||||
|
||||
requireField("product", $product);
|
||||
requireField("platform", $platform);
|
||||
requireField("opsys", $opsys);
|
||||
requireField("branch", $branch);
|
||||
requireField("build_id", $build_id);
|
||||
requireField("locale", $locale);
|
||||
|
||||
# set a cookie with the user's testing details:
|
||||
my $prod = Litmus::DB::Product->retrieve($c->param("product"));
|
||||
my $sysconfig = Litmus::SysConfig->new(
|
||||
$product,
|
||||
$platform,
|
||||
$opsys,
|
||||
$branch,
|
||||
$build_id,
|
||||
$locale
|
||||
);
|
||||
|
||||
return $sysconfig;
|
||||
}
|
||||
1;
|
||||
|
||||
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Terry Weissman <terry@mozilla.org>
|
||||
# Dan Mosedale <dmose@mozilla.org>
|
||||
# Jacob Steenhagen <jake@bugzilla.org>
|
||||
# Bradley Baetz <bbaetz@student.usyd.edu.au>
|
||||
# Christopher Aillon <christopher@aillon.com>
|
||||
# Tobias Burnus <burnus@net-b.de>
|
||||
# Myk Melez <myk@mozilla.org>
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
# This is mostly a placeholder. At some point in the future, we might
|
||||
# want to be more like Bugzilla and support multiple languages and
|
||||
# other fine features, so we keep this around now so that adding new
|
||||
# things later won't require changing every source file.
|
||||
|
||||
package Litmus::Template;
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus::Config;
|
||||
use Litmus::StripScripts;
|
||||
use Text::Markdown;
|
||||
|
||||
use base qw(Template);
|
||||
|
||||
my $template_include_path;
|
||||
|
||||
$Template::Directive::WHILE_MAX = 30000;
|
||||
|
||||
# Returns the path to the templates based on the Accept-Language
|
||||
# settings of the user and of the available languages
|
||||
# If no Accept-Language is present it uses the defined default
|
||||
sub getTemplateIncludePath () {
|
||||
return "templates/en/default";
|
||||
}
|
||||
|
||||
# Constants:
|
||||
my %constants = {};
|
||||
$constants{litmus_version} = $Litmus::Config::version;
|
||||
|
||||
# html tag stripper:
|
||||
my $strip = Litmus::StripScripts->new(
|
||||
{
|
||||
AllowHref => 1,
|
||||
AllowSrc => 1,
|
||||
Context => 'Document'
|
||||
},
|
||||
strict_names => 1,
|
||||
);
|
||||
|
||||
###############################################################################
|
||||
# Templatization Code
|
||||
|
||||
# Use the Toolkit Template's Stash module to add utility pseudo-methods
|
||||
# to template variables.
|
||||
use Template::Stash;
|
||||
|
||||
# Add "contains***" methods to list variables that search for one or more
|
||||
# items in a list and return boolean values representing whether or not
|
||||
# one/all/any item(s) were found.
|
||||
$Template::Stash::LIST_OPS->{ contains } =
|
||||
sub {
|
||||
my ($list, $item) = @_;
|
||||
return grep($_ eq $item, @$list);
|
||||
};
|
||||
|
||||
$Template::Stash::LIST_OPS->{ containsany } =
|
||||
sub {
|
||||
my ($list, $items) = @_;
|
||||
foreach my $item (@$items) {
|
||||
return 1 if grep($_ eq $item, @$list);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
# Allow us to still get the scalar if we use the list operation ".0" on it
|
||||
$Template::Stash::SCALAR_OPS->{ 0 } =
|
||||
sub {
|
||||
return $_[0];
|
||||
};
|
||||
|
||||
# Add a "substr" method to the Template Toolkit's "scalar" object
|
||||
# that returns a substring of a string.
|
||||
$Template::Stash::SCALAR_OPS->{ substr } =
|
||||
sub {
|
||||
my ($scalar, $offset, $length) = @_;
|
||||
return substr($scalar, $offset, $length);
|
||||
};
|
||||
|
||||
# Add a "truncate" method to the Template Toolkit's "scalar" object
|
||||
# that truncates a string to a certain length.
|
||||
$Template::Stash::SCALAR_OPS->{ truncate } =
|
||||
sub {
|
||||
my ($string, $length, $ellipsis) = @_;
|
||||
$ellipsis ||= "";
|
||||
|
||||
return $string if !$length || length($string) <= $length;
|
||||
|
||||
my $strlen = $length - length($ellipsis);
|
||||
my $newstr = substr($string, 0, $strlen) . $ellipsis;
|
||||
return $newstr;
|
||||
};
|
||||
|
||||
# Create the template object that processes templates and specify
|
||||
# configuration parameters that apply to all templates.
|
||||
sub create {
|
||||
my $class = shift;
|
||||
return $class->new({
|
||||
INCLUDE_PATH => &getTemplateIncludePath,
|
||||
CONSTANTS => \%constants,
|
||||
PRE_PROCESS => "variables.none.tmpl",
|
||||
POST_CHOMP => 1,
|
||||
EVAL_PERL => 1,
|
||||
|
||||
COMPILE_DIR => $Litmus::Config::datadir,
|
||||
|
||||
FILTERS => {
|
||||
# disallow all html in testcase data except for non-evil tags
|
||||
testdata => sub {
|
||||
my ($data) = @_;
|
||||
|
||||
$strip->parse($data);
|
||||
$strip->eof();
|
||||
|
||||
return $strip->filtered_document;
|
||||
},
|
||||
|
||||
# process the text with the markdown text processor
|
||||
markdown => sub {
|
||||
my ($data) = @_;
|
||||
$data = Text::Markdown::markdown($data);
|
||||
return $data;
|
||||
},
|
||||
|
||||
# Returns the text with backslashes, single/double quotes,
|
||||
# and newlines/carriage returns escaped for use in JS strings.
|
||||
# thanks to bugzilla!
|
||||
js => sub {
|
||||
my ($var) = @_;
|
||||
$var =~ s/([\\\'\"\/])/\\$1/g;
|
||||
$var =~ s/\n/\\n/g;
|
||||
$var =~ s/\r/\\r/g;
|
||||
$var =~ s/\@/\\x40/g; # anti-spam for email addresses
|
||||
return $var;
|
||||
},
|
||||
|
||||
# anti-spam filtering of email addresses
|
||||
email => sub {
|
||||
my ($var) = @_;
|
||||
$var =~ s/\@/\@/g;
|
||||
return $var;
|
||||
},
|
||||
|
||||
# dummy filter when we don't actually need to filter anything
|
||||
none => sub {
|
||||
my ($var) = @_;
|
||||
return $var;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
# override the process() method to sneak defaultemail into all template
|
||||
# variable spaces
|
||||
sub process {
|
||||
my ($self, $template, $vars, $outstream, @opts) = @_;
|
||||
my %vars = %$vars;
|
||||
|
||||
$vars{defaultemail} = $vars{defaultemail} ? $vars{defaultemail} :
|
||||
Litmus->getCurrentUser();
|
||||
|
||||
$vars{show_admin} = Litmus->getCurrentUser() ?
|
||||
Litmus->getCurrentUser()->is_admin() : 0;
|
||||
|
||||
$self->SUPER::process($template, \%vars, $outstream, @opts);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::Testlist;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Exporter);
|
||||
@Litmus::Testlist::EXPORT = qw(
|
||||
makeHotlist
|
||||
);
|
||||
|
||||
sub makeHotlist {
|
||||
my $numtests = shift;
|
||||
my $state = shift;
|
||||
my @tests = @_;
|
||||
|
||||
my @potentialtests;
|
||||
foreach my $curtest (@tests) {
|
||||
if ($curtest->isrecent()) {
|
||||
foreach my $curplat ($curtest->subgroup()->testgroup()->product()->platforms()) {
|
||||
my $found;
|
||||
my $curstate = $curtest->state($curplat);
|
||||
if ($curstate && $curstate->resultid() == $state->resultid()) {
|
||||
push(@potentialtests, $curtest);
|
||||
$found++;
|
||||
}
|
||||
if ($found) {next}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@potentialtests = sort{$b->num_recent_results() <=> $a->num_recent_results()}
|
||||
@potentialtests;
|
||||
|
||||
return @potentialtests;
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
# Handle detection of system information from the UA string
|
||||
|
||||
package Litmus::UserAgentDetect;
|
||||
|
||||
use strict;
|
||||
|
||||
require Exporter;
|
||||
use Litmus;
|
||||
use Litmus::DB::Platform;
|
||||
use Litmus::DB::Opsys;
|
||||
use Litmus::DB::Branch;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT = qw(detectBuildID);
|
||||
|
||||
# define some SQL queries we will use:
|
||||
Litmus::DB::Platform->set_sql(detectplatform => qq{
|
||||
SELECT p.platform_id
|
||||
FROM platforms p, platform_products pp
|
||||
WHERE
|
||||
? REGEXP detect_regexp AND
|
||||
p.platform_id=pp.platform_id AND
|
||||
pp.product_id LIKE ?
|
||||
});
|
||||
Litmus::DB::Branch->set_sql(detectbranch => qq{
|
||||
SELECT __ESSENTIAL__
|
||||
FROM __TABLE__
|
||||
WHERE
|
||||
? REGEXP detect_regexp AND
|
||||
product_id LIKE ?
|
||||
});
|
||||
|
||||
# constructor. Optionally you can pass a UA string
|
||||
# and it will be used. Otherwise the default is the
|
||||
# current useragent.
|
||||
sub new {
|
||||
my $self = {};
|
||||
my $class = shift;
|
||||
my $ua = shift;
|
||||
|
||||
bless($self);
|
||||
$self->{ua} = $main::ENV{"HTTP_USER_AGENT"};
|
||||
if ($ua) { $self->{ua} = $ua }
|
||||
return $self;
|
||||
}
|
||||
|
||||
# default stringification is to return the ua:
|
||||
use overload
|
||||
'""' => \&ua;
|
||||
|
||||
|
||||
sub ua {
|
||||
my $self = shift;
|
||||
|
||||
# we pad the UA with a space since some of our regexp matches expect
|
||||
# to match things at the end of the string. This is quite possibly
|
||||
# a bug.
|
||||
return $self->{ua}." ";
|
||||
}
|
||||
|
||||
sub build_id {
|
||||
my $self = shift;
|
||||
my $ua = $self->{ua};
|
||||
|
||||
# mozilla products only
|
||||
unless ($ua =~ /Mozilla\/5\.0/) {
|
||||
return undef;
|
||||
}
|
||||
$ua =~ /(200\d*)/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub locale {
|
||||
my $self = shift;
|
||||
my $ua = $self->{ua};
|
||||
|
||||
# mozilla products only
|
||||
unless ($ua =~ /Mozilla\/5\.0/) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
# Format (e.g.):
|
||||
# Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20051111 Firefox/1.5
|
||||
$ua =~ /Mozilla\/5\.0 \([^;]*; [^;]*; [^;]*; ([^;]*); [^;]*\)/;
|
||||
return $1;
|
||||
}
|
||||
|
||||
sub platform {
|
||||
my $self = shift;
|
||||
my $product = shift; # optionally, just lookup for one product
|
||||
|
||||
if (! $product) { $product = '%' }
|
||||
|
||||
my @platforms = Litmus::DB::Platform->search_detectplatform($self->ua, $product);
|
||||
return @platforms;
|
||||
}
|
||||
|
||||
sub branch {
|
||||
my $self = shift;
|
||||
my $product = shift; # optionally, just lookup for one branch
|
||||
|
||||
if (! $product) { $product = '%' }
|
||||
|
||||
my @branches = Litmus::DB::Branch->search_detectbranch($self->ua, $product);
|
||||
return @branches;
|
||||
}
|
||||
|
||||
# from the legacy API before we had an OO interface:
|
||||
sub detectBuildId() {
|
||||
my $self = Litmus::UserAgentDetect->new($main::ENV{"HTTP_USER_AGENT"});
|
||||
|
||||
return $self->build_id();
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
# General utility functions
|
||||
|
||||
package Litmus::Utils;
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Error;
|
||||
use CGI;
|
||||
|
||||
our @ISA = qw(Exporter);
|
||||
@Litmus::Utils::EXPORT = qw(
|
||||
requireField
|
||||
);
|
||||
|
||||
|
||||
# requireField - checks that $field contains data (other than ---) and throws
|
||||
# an invalidInputError if it does not.
|
||||
sub requireField {
|
||||
my ($fieldname, $field) = @_;
|
||||
|
||||
unless($field && $field ne "---") {
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
invalidInputError("You must make a valid selection for field ".$fieldname.".");
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,419 +0,0 @@
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License
|
||||
# Version 1.1 (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License
|
||||
# at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS"
|
||||
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
||||
# the License for the specific language governing rights and
|
||||
# limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
=cut
|
||||
|
||||
package Litmus::XML;
|
||||
|
||||
# Litmus XML Interface
|
||||
# For further details, see the web services specification at
|
||||
# http://wiki.mozilla.org/Litmus:Web_Services
|
||||
|
||||
use strict;
|
||||
|
||||
use XML::XPath;
|
||||
use XML::XPath::XMLParser;
|
||||
|
||||
use Litmus::DB::User;
|
||||
use Litmus::UserAgentDetect;
|
||||
use Date::Manip;
|
||||
|
||||
use CGI::Carp qw(set_message fatalsToBrowser);
|
||||
|
||||
# if we die for some reason, make sure we give a fatal error per spec
|
||||
BEGIN {
|
||||
set_message(sub {
|
||||
print "Fatal error: internal server error\n";
|
||||
});
|
||||
}
|
||||
|
||||
no warnings;
|
||||
no diagnostics;
|
||||
|
||||
sub new {
|
||||
my $self = {};
|
||||
bless($self);
|
||||
return $self;
|
||||
}
|
||||
|
||||
# process XML test result data as described by
|
||||
# the spec at http://wiki.mozilla.org/Litmus:Web_Services
|
||||
sub processResults {
|
||||
my $self = shift;
|
||||
my $data = shift;
|
||||
|
||||
$self->parseResultFile($data) ? 1 : return 0;
|
||||
|
||||
unless ($self->authenticate()) { return 0} # login failure
|
||||
|
||||
$self->validateResults() ? 1 : return 0;
|
||||
|
||||
# at this point, everything is valid, so if we're just validating the
|
||||
# results, we can return an ok:
|
||||
if ($self->{'action'} eq 'validate') {
|
||||
unless ($self->{'response'}) { $self->respOk() }
|
||||
return 1;
|
||||
}
|
||||
|
||||
# add so-called 'global logs' that apply to all the results
|
||||
# we save them in @globallogs so we can map them to the results later
|
||||
my @globallogs;
|
||||
foreach my $log (@{$self->{'logs'}}) {
|
||||
# the submission time is the timestamp of the first testresult:
|
||||
my $newlog = Litmus::DB::Log->create({
|
||||
submission_time => $self->{'results'}->[0]->{'timestamp'},
|
||||
log_type => $log->{'type'},
|
||||
log_text => $log->{'data'},
|
||||
});
|
||||
push(@globallogs, $newlog);
|
||||
}
|
||||
|
||||
# now actually add the new results to the db:
|
||||
foreach my $result (@{$self->{'results'}}) {
|
||||
my $newres = Litmus::DB::Testresult->create({
|
||||
testcase => $result->{'testid'},
|
||||
user_agent => new Litmus::UserAgentDetect($self->{'useragent'}),
|
||||
user => $self->{'user'},
|
||||
opsys => $self->{'sysconfig'}->{'opsys'},
|
||||
branch => $self->{'sysconfig'}->{'branch'},
|
||||
locale => $self->{'sysconfig'}->{'locale'},
|
||||
build_id => $self->{'sysconfig'}->{'buildid'},
|
||||
machine_name => $self->{'machinename'},
|
||||
result_status => $result->{'resultstatus'},
|
||||
timestamp => $result->{'timestamp'},
|
||||
exit_status => $result->{'exitstatus'},
|
||||
duration_ms => $result->{'duration'},
|
||||
valid => 1,
|
||||
isAutomated => $result->{'isAutomated'},
|
||||
});
|
||||
|
||||
if (!$newres) { $self->respErrResult($result->{'testid'}); next; }
|
||||
|
||||
# add any bug ids:
|
||||
foreach my $bug (@{$result->{'bugs'}}) {
|
||||
my $newbug = Litmus::DB::Resultbug->create({
|
||||
test_result_id => $newres,
|
||||
bug_id => $bug,
|
||||
submission_time => $result->{'timestamp'},
|
||||
user => $self->{'user'},
|
||||
});
|
||||
}
|
||||
|
||||
# add any comments:
|
||||
foreach my $comment (@{$result->{'comments'}}) {
|
||||
my $newcomment = Litmus::DB::Comment->create({
|
||||
test_result => $newres,
|
||||
submission_time => $result->{'timestamp'},
|
||||
user => $self->{'user'},
|
||||
comment => $comment,
|
||||
});
|
||||
}
|
||||
|
||||
# add logs:
|
||||
my @resultlogs;
|
||||
push(@resultlogs, @globallogs); # all results get the global logs
|
||||
foreach my $log (@{$result->{'logs'}}) {
|
||||
my $newlog = Litmus::DB::Log->create({
|
||||
submission_time => $result->{'timestamp'},
|
||||
log_type => $log->{'type'},
|
||||
log_text => $log->{'data'},
|
||||
});
|
||||
push(@resultlogs, $newlog);
|
||||
}
|
||||
|
||||
# now we map the logs to the current result:
|
||||
foreach my $log (@resultlogs) {
|
||||
Litmus::DB::LogTestresult->create({
|
||||
test_result => $newres,
|
||||
log_id => $log,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unless ($self->{'response'}) { $self->respOk() }
|
||||
|
||||
#$self->{'response'} = $self->{'results'}->[0]->{'resultstatus'};
|
||||
|
||||
}
|
||||
|
||||
sub parseResultFile {
|
||||
my $self = shift;
|
||||
my $data = shift;
|
||||
my $x = XML::XPath->new(xml => $data, standalone => 1);
|
||||
$self->{'useragent'} = $x->findvalue('/litmusresults/@useragent');
|
||||
$self->{'machinename'} = $x->findvalue('litmusresults/@machinename');
|
||||
$self->{'action'} = $x->findvalue('/litmusresults/@action');
|
||||
$self->{'user'}->{'username'} = $x->findvalue(
|
||||
'/litmusresults/testresults/@username');
|
||||
$self->{'user'}->{'token'} = $x->findvalue(
|
||||
'/litmusresults/testresults/@authtoken');
|
||||
|
||||
$self->{'sysconfig'}->{'product'} = $x->findvalue('/litmusresults/testresults/@product');
|
||||
$self->{'sysconfig'}->{'platform'} = $x->findvalue('/litmusresults/testresults/@platform');
|
||||
$self->{'sysconfig'}->{'opsys'} = $x->findvalue('/litmusresults/testresults/@opsys');
|
||||
$self->{'sysconfig'}->{'branch'} = $x->findvalue('/litmusresults/testresults/@branch');
|
||||
$self->{'sysconfig'}->{'buildid'} = $x->findvalue('/litmusresults/testresults/@buildid');
|
||||
$self->{'sysconfig'}->{'locale'} = $x->findvalue('/litmusresults/testresults/@locale');
|
||||
|
||||
my @glogs = $x->find('/litmusresults/testresults/log')->get_nodelist();
|
||||
my $l_ct = 0;
|
||||
foreach my $log (@glogs) {
|
||||
my $type = $x->findvalue('@logtype', $log);
|
||||
my $logdata = stripWhitespace($log->string_value());
|
||||
$self->{'logs'}->[$l_ct]->{'type'} = $type;
|
||||
$self->{'logs'}->[$l_ct]->{'data'} = $logdata;
|
||||
$l_ct++;
|
||||
}
|
||||
|
||||
my @results = $x->find('/litmusresults/testresults/result')->get_nodelist();
|
||||
my $c = 0;
|
||||
foreach my $result (@results) {
|
||||
$self->{'results'}->[$c]->{'testid'} = $x->findvalue('@testid', $result);
|
||||
$self->{'results'}->[$c]->{'isAutomated'} = $x->findvalue('@is_automated_result', $result);
|
||||
$self->{'results'}->[$c]->{'resultstatus'} = $x->findvalue('@resultstatus', $result);
|
||||
$self->{'results'}->[$c]->{'exitstatus'} = $x->findvalue('@exitstatus', $result);
|
||||
$self->{'results'}->[$c]->{'duration'} = $x->findvalue('@duration', $result);
|
||||
$self->{'results'}->[$c]->{'timestamp'} =
|
||||
&Date::Manip::UnixDate($x->findvalue('@timestamp', $result), "%q");
|
||||
|
||||
|
||||
my @comments = $x->find('comment', $result)->get_nodelist();
|
||||
my $com_ct = 0;
|
||||
foreach my $comment (@comments) {
|
||||
$comment = stripWhitespace($comment->string_value());
|
||||
$self->{'results'}->[$c]->{'comments'}->[$com_ct] = $comment;
|
||||
$com_ct++;
|
||||
}
|
||||
|
||||
my @bugs = $x->find('bugnumber', $result)->get_nodelist();
|
||||
my $bug_ct = 0;
|
||||
foreach my $bug (@bugs) {
|
||||
$bug = stripWhitespace($bug->string_value());
|
||||
$self->{'results'}->[$c]->{'bugs'}->[$bug_ct];
|
||||
$bug_ct++;
|
||||
}
|
||||
|
||||
my @logs = $x->find('log', $result)->get_nodelist();
|
||||
my $log_ct = 0;
|
||||
foreach my $log (@logs) {
|
||||
my $type = $x->findvalue('@logtype', $log);
|
||||
my $logdata = stripWhitespace($log->string_value());
|
||||
$self->{'results'}->[$c]->{'logs'}->[$log_ct]->{'type'} = $type;
|
||||
$self->{'results'}->[$c]->{'logs'}->[$log_ct]->{'data'} = $logdata;
|
||||
$log_ct++;
|
||||
}
|
||||
$c++;
|
||||
}
|
||||
|
||||
$self->{'x'} = $x;
|
||||
}
|
||||
|
||||
# validate the result data, and resolve references to various tables
|
||||
# the correct objects, looking up id numbers as needed
|
||||
sub validateResults {
|
||||
my $self = shift;
|
||||
|
||||
my $action = $self->{'action'};
|
||||
if ($action ne 'submit' && $action ne 'validate') {
|
||||
$self->respErrFatal("Action must be either 'submit' or 'validate'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my @users = Litmus::DB::User->search(email => $self->{'user'}->{'username'});
|
||||
$self->{'user'} = $users[0];
|
||||
|
||||
|
||||
unless ($self->{'useragent'}) {
|
||||
$self->respErrFatal("You must specify a useragent");
|
||||
return 0;
|
||||
}
|
||||
|
||||
my @prods = Litmus::DB::Product->search(name => $self->{'sysconfig'}->{'product'});
|
||||
unless ($prods[0]) {
|
||||
$self->respErrFatal("Invalid product: ".$self->{'sysconfig'}->{'product'});
|
||||
return 0;
|
||||
}
|
||||
$self->{'sysconfig'}->{'product'} = $prods[0];
|
||||
|
||||
my @platforms = Litmus::DB::Platform->search_ByProductAndName(
|
||||
$self->{'sysconfig'}->{'product'},
|
||||
$self->{'sysconfig'}->{'platform'});
|
||||
unless ($platforms[0]) {
|
||||
$self->respErrFatal("Invalid platform: ".$self->{'sysconfig'}->{'platform'});
|
||||
return 0;
|
||||
}
|
||||
$self->{'sysconfig'}->{'platform'} = $platforms[0];
|
||||
|
||||
my @opsyses = Litmus::DB::Opsys->search(
|
||||
name => $self->{'sysconfig'}->{'opsys'},
|
||||
platform => $self->{'sysconfig'}->{'platform'});
|
||||
unless ($opsyses[0]) {
|
||||
$self->respErrFatal("Invalid opsys: ".$self->{'sysconfig'}->{'opsys'});
|
||||
return 0;
|
||||
}
|
||||
$self->{'sysconfig'}->{'opsys'} = $opsyses[0];
|
||||
|
||||
my @branches = Litmus::DB::Branch->search(
|
||||
name => $self->{'sysconfig'}->{'branch'},
|
||||
product => $self->{'sysconfig'}->{'product'});
|
||||
unless ($branches[0]) {
|
||||
$self->respErrFatal("Invalid branch: ".$self->{'sysconfig'}->{'branch'});
|
||||
return 0;
|
||||
}
|
||||
$self->{'sysconfig'}->{'branch'} = $branches[0];
|
||||
|
||||
unless ($self->{'sysconfig'}->{'buildid'}) {
|
||||
$self->respErrFatal("Invalid build id: ".$self->{'sysconfig'}->{'buildid'});
|
||||
return 0;
|
||||
}
|
||||
|
||||
my @locales = Litmus::DB::Locale->search(
|
||||
locale => $self->{'sysconfig'}->{'locale'});
|
||||
unless ($locales[0]) {
|
||||
$self->respErrFatal("Invalid locale: ".$self->{'sysconfig'}->{'locale'});
|
||||
return 0;
|
||||
}
|
||||
$self->{'sysconfig'}->{'locale'} = $locales[0];
|
||||
|
||||
foreach my $log (@{$self->{'logs'}}) {
|
||||
my @types = Litmus::DB::LogType->search(name => $log->{'type'});
|
||||
unless ($types[0]) {
|
||||
$self->respErrFatal("Invalid log type: ".$log->{'type'});
|
||||
return 0;
|
||||
}
|
||||
$log->{'type'} = $types[0];
|
||||
}
|
||||
|
||||
foreach my $result (@{$self->{'results'}}) {
|
||||
my @tests = Litmus::DB::Testcase->search(
|
||||
test_id => $result->{'testid'});
|
||||
unless ($tests[0]) {
|
||||
$self->respErrResult('unknown', "Invalid test id");
|
||||
next;
|
||||
}
|
||||
$result->{'testid'} = $tests[0];
|
||||
|
||||
# assume it's an automated test result if not specified
|
||||
($result->{'isAutomated'} eq '0' || $result->{'isAutomated'} ne undef) ?
|
||||
$result->{'isAutomated'} = 0 : $result->{'isAutomated'} = 1;
|
||||
|
||||
my @results = Litmus::DB::ResultStatus->search(
|
||||
name => $result->{'resultstatus'});
|
||||
unless ($results[0]) {
|
||||
$self->respErrResult($result->{'testid'}, "Invalid resultstatus");
|
||||
next;
|
||||
}
|
||||
$result->{'resultstatus'} = $results[0];
|
||||
|
||||
my @es = Litmus::DB::ExitStatus->search(
|
||||
name => $result->{'exitstatus'});
|
||||
unless ($es[0]) {
|
||||
$self->respErrResult($result->{'testid'}, "Invalid exitstatus");
|
||||
next;
|
||||
}
|
||||
$result->{'exitstatus'} = $es[0];
|
||||
|
||||
# if there's no duration, then it's just 0:
|
||||
unless ($result->{'duration'}) {
|
||||
$result->{'duration'} = 0;
|
||||
}
|
||||
|
||||
# if there's no timestamp, then it's now:
|
||||
unless ($result->{'timestamp'}) {
|
||||
$result->{'timestamp'} = &Date::Manip::UnixDate("now","%q");
|
||||
}
|
||||
|
||||
foreach my $log (@{$result->{'logs'}}) {
|
||||
my @types = Litmus::DB::LogType->search(name => $log->{'type'});
|
||||
unless ($types[0]) {
|
||||
$self->respErrResult($result->{'testid'},
|
||||
"Invalid log type: ".$log->{'type'});
|
||||
next;
|
||||
}
|
||||
$log->{'type'} = $types[0];
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub response {
|
||||
my $self = shift;
|
||||
return $self->{'response'};
|
||||
}
|
||||
|
||||
|
||||
# ONLY NON-PUBLIC API BELOW THIS POINT
|
||||
|
||||
sub authenticate {
|
||||
my $self = shift;
|
||||
|
||||
my @users = Litmus::DB::User->search(email => $self->{'user'}->{'username'});
|
||||
my $user = $users[0];
|
||||
|
||||
unless ($user) { $self->respErrFatal("User does not exist"); return 0 }
|
||||
|
||||
unless ($user->enabled()) { $self->respErrFatal("User disabled"); return 0 }
|
||||
|
||||
if ($user->authtoken() ne $self->{'user'}->{'token'}) {
|
||||
respErrFatal("Invalid authentication token for user ".
|
||||
$self->{'user'}->{'username'});
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub respOk {
|
||||
my $self = shift;
|
||||
$self->{'response'} = 'ok';
|
||||
}
|
||||
|
||||
sub respErrFatal {
|
||||
my $self = shift;
|
||||
my $error = shift;
|
||||
$self->{'response'} = "Fatal error: $error\n";
|
||||
}
|
||||
|
||||
sub respErrResult {
|
||||
my $self = shift;
|
||||
my $testid = shift;
|
||||
my $error = shift;
|
||||
$self->{'response'} .= "Error processing result for test $testid: $error\n";
|
||||
}
|
||||
|
||||
# remove leading and trailing whitespace from logs and comments
|
||||
sub stripWhitespace {
|
||||
my $txt = shift;
|
||||
$txt =~ s/^\s+//;
|
||||
$txt =~ s/\s+$//;
|
||||
return $txt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
1;
|
||||
@@ -1,56 +0,0 @@
|
||||
# Litmus Makefile
|
||||
|
||||
PERL=perl
|
||||
|
||||
install: templates
|
||||
$(PERL) populatedb.pl
|
||||
|
||||
# precompile all templates with the Template Toolkit
|
||||
# to speed things up a good bit.
|
||||
# This ought to be done in a more "makelike" way, but
|
||||
# various difficulties prevent that unless we use a configure
|
||||
# script to generate the Makefile, at which point we could have
|
||||
# been done already...
|
||||
|
||||
%.tmpl:
|
||||
$(PERL) -e " \
|
||||
eval('use CGI qw(-no_debug)'); \
|
||||
use Litmus::Template;use diagnostics; \
|
||||
\$$template = Litmus::Template->create(); \
|
||||
\$$template->context()->template('$@'); \
|
||||
"
|
||||
|
||||
templates: index.html.tmpl
|
||||
$(PERL) -e " \
|
||||
use File::Find; \
|
||||
find({ wanted => sub { \
|
||||
\$$name = \$$File::Find::name; \
|
||||
return if (-d \$$name); \
|
||||
return if (\$$name =~ /\/CVS\//); \
|
||||
return if (\$$name !~ /\.tmpl\$$/); \
|
||||
\$$name =~ s/templates\/en\/default\///; \
|
||||
if (-M 'templates/en/default/'.\$$name < -M 'data/templates/en/default/'.\$$name \
|
||||
|| ! -e 'data/templates/en/default/'.\$$name \
|
||||
|| -M 'Litmus/Template.pm' < -M 'data/templates/en/default/'.\$$name) { \
|
||||
system("make", "\$$name"); \
|
||||
} \
|
||||
}, no_chdir => 1 }, 'templates/en/default'); \
|
||||
"
|
||||
|
||||
# tags: generate ctags style hints for ease of editing
|
||||
# requires Exuberant Ctags to be installed (http://ctags.sf.net/)
|
||||
ctags:
|
||||
`which ctags` --excmd=number --tag-relative=no --fields=+a+m+n+S -R `pwd`
|
||||
|
||||
tags: ctags
|
||||
|
||||
test:
|
||||
$(PERL) runtests.pl
|
||||
|
||||
cache:
|
||||
$(PERL) -MLitmus -MLitmus::Cache -e "Litmus::Cache::rebuildCache();"
|
||||
@echo "Done";
|
||||
|
||||
clean:
|
||||
rm -rf data
|
||||
make install
|
||||
@@ -1,47 +0,0 @@
|
||||
===Litmus===
|
||||
|
||||
If you're reading this, you've downloaded, received, or simply conjured
|
||||
out of thin air, a copy of the Litmus testcase management system.
|
||||
Presumably, you're reading this file because you have some sort of
|
||||
question about Litmus. Hopefully, if we've done our job right, this file
|
||||
ought to answer your questions.
|
||||
|
||||
Q: What is Litmus?
|
||||
A: Litmus is a testcase management system. Its goal is to allow users to
|
||||
enter software tests, run them, and view and manage the results. Along
|
||||
the way, users can expect to be able to do queries and reports and have
|
||||
access all the usual features they expect from a first-class web
|
||||
application. The reality may be somewhat different than this goal.
|
||||
Litmus is developed by mozilla.org.
|
||||
|
||||
Q: How do I install this dang thing?
|
||||
A: You probably want the file called INSTALL.
|
||||
|
||||
Q: Where is the real documentation?
|
||||
A: Hahahaha. What is this "documentation" you speak of? You might want
|
||||
to check out the Litmus Wiki, which may or may not contain useful
|
||||
information. See http://wiki.mozilla.org/Litmus.
|
||||
|
||||
Q: What needs to be done?
|
||||
A: See http://wiki.mozilla.org/Litmus:Todo
|
||||
|
||||
Q: How much does it cost?
|
||||
A: Nothing. Litmus is Free Software, licensed under the Mozilla Public
|
||||
License.
|
||||
|
||||
Q: Wait. Isn't "testcase" two words?
|
||||
A: Not here it isn't.
|
||||
|
||||
Q: Waaaaaaah. Why is Litmus written in Perl and not
|
||||
PHP/Python/Java/Objective Pascal/Latin?
|
||||
A: Because I know Perl. Duh. Also because Litmus uses some code from
|
||||
Bugzilla, and it wouldn't be able to do this if it was written in some
|
||||
other language. Camels are also some of the least buggy animals around,
|
||||
as they swat flies away with their tails.
|
||||
|
||||
Q: I'm still confused. You didn't answer my question. I don't know what
|
||||
to do. Help!
|
||||
A: First of all, that's not a question. In any case, your best bet is
|
||||
probably to email Zach Lipton <zach@zachlipton.com>, and if you ask
|
||||
nicely and don't make too much of a pest of yourself, he'd be glad to
|
||||
get you on the right track.
|
||||
@@ -1,246 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Error;
|
||||
use Litmus::DB::Testresult;
|
||||
use Litmus::FormWidget;
|
||||
|
||||
use CGI;
|
||||
use Time::Piece::MySQL;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
|
||||
|
||||
|
||||
# Hash refs for maintaining state in the search form.
|
||||
my $defaults = undef;
|
||||
my $order_bys = undef;
|
||||
|
||||
my $MAX_SORT_FIELDS = 10;
|
||||
my $MAX_SEARCH_FIELDS = 10;
|
||||
|
||||
my $criteria = "Custom<br/>";
|
||||
my $results;
|
||||
my @where;
|
||||
my @order_by;
|
||||
my $limit;
|
||||
my $where_criteria = "";
|
||||
my $order_by_criteria = "";
|
||||
my $limit_criteria = "";
|
||||
|
||||
if ($c->param) {
|
||||
|
||||
foreach my $param ($c->param) {
|
||||
next if ($c->param($param) eq '');
|
||||
|
||||
if ($param =~ /sort_field(\d+)/) {
|
||||
# We slot sort fields into the @order_by array based on their
|
||||
# field_num. Empty array slots will be ignored when the SQL
|
||||
# is built. We set an upper limit on the number of sort fields
|
||||
# we can handle to prevent abuse.
|
||||
my $field_num = $1;
|
||||
next if ($field_num > $MAX_SORT_FIELDS);
|
||||
my $sort_field = $c->param($param);
|
||||
my $sort_order = 'ASC';
|
||||
if ($c->param("sort_order$field_num")) {
|
||||
$sort_order = $c->param("sort_order$field_num");
|
||||
}
|
||||
$order_by[$field_num] = { field => $sort_field,
|
||||
direction => $sort_order};
|
||||
|
||||
} elsif ($param =~ /search_field(\d+)/) {
|
||||
# We set an upper limit on the number of search fields
|
||||
# we can handle to prevent abuse.
|
||||
my $field_num = $1;
|
||||
next if ($field_num > $MAX_SEARCH_FIELDS);
|
||||
my $search_field = $c->param($param);
|
||||
my $match_criteria = $c->param("match_criteria$field_num");
|
||||
my $value = $c->param("search_value$field_num");
|
||||
push @where, { 'field' => 'search_field',
|
||||
'search_field' => $search_field,
|
||||
'match_criteria' => $match_criteria,
|
||||
'value' => $value};
|
||||
$where_criteria .= "$search_field $match_criteria '$value'<br/>";
|
||||
|
||||
} elsif ($param eq 'start_date') {
|
||||
my $start_date = $c->param($param);
|
||||
$start_date =~ s/[^0-9A-Za-z ]/ /g;
|
||||
my $end_date;
|
||||
# Use 'now' as the default end date.
|
||||
if ($c->param('end_date') and $c->param('end_date') ne '') {
|
||||
$end_date = $c->param('end_date');
|
||||
$end_date =~ s/[^0-9A-Za-z ]/ /g;
|
||||
} else {
|
||||
$end_date = 'Now';
|
||||
}
|
||||
push @where, { field => 'start_date',
|
||||
value => $start_date};
|
||||
push @where, { field => 'end_date',
|
||||
value => $end_date};
|
||||
$where_criteria .= "Date between '$start_date' and '$end_date'<br/>";
|
||||
} elsif ($param eq 'trusted_only') {
|
||||
push @where, {field => 'trusted_only',
|
||||
value => 1};
|
||||
$limit_criteria .= "Display trusted results only<br/>";
|
||||
} elsif ($param eq "limit") {
|
||||
$limit = $c->param($param);
|
||||
next if ($limit == $Litmus::DB::Testresult::_num_results_default);
|
||||
$limit_criteria .= "Limit to $limit results<br/>";
|
||||
} elsif ($param eq 'branch') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Branch is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{branch} = $c->param($param);
|
||||
} elsif ($param eq 'locale') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => 'locale',
|
||||
value => $value};
|
||||
$where_criteria .= "Locale is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{locale} = $c->param($param);
|
||||
} elsif ($param eq 'email') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => 'email',
|
||||
value => $value};
|
||||
$where_criteria .= "Submitted By is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{locale} = $c->param($param);
|
||||
} elsif ($param eq 'product') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Product is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{product} = $c->param($param);
|
||||
} elsif ($param eq 'platform') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Platform is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{platform} = $c->param($param);
|
||||
} elsif ($param eq 'test_group') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Test group is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{test_group} = $c->param($param);
|
||||
} elsif ($param eq 'test_id') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Testcase ID# is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{test_id} = $c->param($param);
|
||||
} elsif ($param eq 'summary') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Summary like \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{summary} = $c->param($param);
|
||||
} elsif ($param eq 'result_status') {
|
||||
my $value = $c->param($param);
|
||||
push @where, {field => $param,
|
||||
value => $value};
|
||||
$where_criteria .= "Status is \'".$c->param($param)."\'<br/>";
|
||||
$defaults->{result_status} = $c->param($param);
|
||||
} else {
|
||||
# Skip unknown field
|
||||
}
|
||||
}
|
||||
if ($where_criteria eq '' and
|
||||
scalar(@order_by) == 0 and
|
||||
$limit_criteria eq '') {
|
||||
($criteria,$results) =
|
||||
Litmus::DB::Testresult->getDefaultTestResults;
|
||||
} else {
|
||||
foreach my $order_by_field (@order_by) {
|
||||
next if (!$order_by_field);
|
||||
$order_by_criteria .= "Order by $order_by_field->{field} $order_by_field->{direction}<br/>";
|
||||
}
|
||||
|
||||
$criteria .= $where_criteria . $order_by_criteria . $limit_criteria;
|
||||
$criteria =~ s/_/ /g;
|
||||
$results = Litmus::DB::Testresult->getTestResults(\@where,
|
||||
\@order_by,
|
||||
$limit);
|
||||
}
|
||||
} else {
|
||||
($criteria,$results) =
|
||||
Litmus::DB::Testresult->getDefaultTestResults;
|
||||
}
|
||||
|
||||
# Populate each of our form widgets for select/input.
|
||||
# Set a default value as appropriate.
|
||||
my $products = Litmus::FormWidget->getProducts;
|
||||
my $platforms = Litmus::FormWidget->getUniquePlatforms;
|
||||
my $test_groups = Litmus::FormWidget->getTestgroups;
|
||||
my $testcases = Litmus::FormWidget->getTestcaseIDs;
|
||||
my $result_statuses = Litmus::FormWidget->getResultStatuses;
|
||||
my $branches = Litmus::FormWidget->getBranches;
|
||||
my $locales = Litmus::FormWidget->getLocales;
|
||||
my $users = Litmus::FormWidget->getUsers;
|
||||
|
||||
my $fields = Litmus::FormWidget->getFields;
|
||||
my $match_criteria = Litmus::FormWidget->getMatchCriteria;
|
||||
my $sort_fields = Litmus::FormWidget->getSortFields;
|
||||
|
||||
my $title = 'Advanced Search';
|
||||
|
||||
my $vars = {
|
||||
title => $title,
|
||||
criteria => $criteria,
|
||||
products => $products,
|
||||
platforms => $platforms,
|
||||
test_groups => $test_groups,
|
||||
testcases => $testcases,
|
||||
result_statuses => $result_statuses,
|
||||
branches => $branches,
|
||||
locales => $locales,
|
||||
users => $users,
|
||||
fields => $fields,
|
||||
match_criteria => $match_criteria,
|
||||
sort_fields => $sort_fields,
|
||||
};
|
||||
|
||||
# Only include results if we have them.
|
||||
if ($results and scalar @$results > 0) {
|
||||
$vars->{results} = $results;
|
||||
}
|
||||
|
||||
my $cookie = Litmus::Auth::getCookie();
|
||||
$vars->{"defaultemail"} = $cookie;
|
||||
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);
|
||||
|
||||
Litmus->template()->process("reporting/advanced_search.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit 0;
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Error;
|
||||
use Litmus::DB::Testresult;
|
||||
use Litmus::FormWidget;
|
||||
|
||||
|
||||
use CGI;
|
||||
use Time::Piece::MySQL;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
|
||||
my $results;
|
||||
if ($c->param and $c->param('status')) {
|
||||
if ($c->param('status') =~ /pass/i or
|
||||
$c->param('status') =~ /fail/i or
|
||||
$c->param('status') =~ /unclear/i) {
|
||||
$results = Litmus::DB::Testresult->getCommonResults($c->param('status'));
|
||||
} else {
|
||||
internalError("You must provide a valid status type: pass|fail|unclear");
|
||||
exit 1;
|
||||
}
|
||||
} else {
|
||||
internalError("You must provide a status type: pass|fail|unclear");
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $title;
|
||||
if ($c->param('status') eq 'pass') {
|
||||
$title = "Most Commonly Passed Testcases";
|
||||
} elsif ($c->param('status') eq 'fail') {
|
||||
$title = "Most Common Failures";
|
||||
} elsif ($c->param('status') eq 'unclear') {
|
||||
$title = "Testcases Most Frequently Marked As Unclear";
|
||||
}
|
||||
|
||||
my $vars = {
|
||||
title => $title,
|
||||
status => $c->param('status'),
|
||||
};
|
||||
|
||||
# Only include results if we have them.
|
||||
if ($results and scalar @$results > 0) {
|
||||
$vars->{results} = $results;
|
||||
}
|
||||
|
||||
$vars->{"defaultemail"} = Litmus::Auth::getCookie();
|
||||
|
||||
Litmus->template()->process("reporting/common_results.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 791 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 261 B |
|
Before Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 396 B |
@@ -1,105 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Zach Lipton <zach@zachlipton.com>
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Error;
|
||||
use Litmus::DB::Product;
|
||||
use Litmus::DB::TestcaseSubgroup;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Utils;
|
||||
|
||||
use CGI;
|
||||
use Time::Piece::MySQL;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
# obviously, you need to be an admin to edit users...
|
||||
Litmus::Auth::requireAdmin('edit_users.cgi');
|
||||
|
||||
if ($c->param('search_string')) {
|
||||
# search for users:
|
||||
my $users = Litmus::DB::User->search_FullTextMatches(
|
||||
$c->param('search_string'),
|
||||
$c->param('search_string'),
|
||||
$c->param('search_string'));
|
||||
my $vars = {
|
||||
users => $users,
|
||||
};
|
||||
print $c->header();
|
||||
Litmus->template()->process("admin/edit_users/search_results.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
} elsif ($c->param('id')) {
|
||||
# lookup a given user
|
||||
my $uid = $c->param('id');
|
||||
my $user = Litmus::DB::User->retrieve($uid);
|
||||
print $c->header();
|
||||
if (! $user) {
|
||||
invalidInputError("Invalid user id: $uid");
|
||||
}
|
||||
my $vars = {
|
||||
user => $user,
|
||||
};
|
||||
Litmus->template()->process("admin/edit_users/edit_user.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
} elsif ($c->param('user_id')) {
|
||||
# process changes to a user:
|
||||
my $user = Litmus::DB::User->retrieve($c->param('user_id'));
|
||||
print $c->header();
|
||||
if (! $user) {
|
||||
invalidInputError("Invalid user id: " . $c->param('user_id'));
|
||||
}
|
||||
$user->bugzilla_uid($c->param('bugzilla_uid'));
|
||||
$user->email($c->param('edit_email'));
|
||||
|
||||
if ($c->param('edit_password') ne 'unchanged') {
|
||||
# they changed the password, so let the auth folks know:
|
||||
Litmus::Auth::changePassword($user, $c->param('edit_password'));
|
||||
}
|
||||
$user->realname($c->param('realname'));
|
||||
$user->irc_nickname($c->param('irc_nickname'));
|
||||
if ($c->param('enabled')) {
|
||||
$user->enabled(1);
|
||||
}
|
||||
if ($c->param('is_admin')) {
|
||||
$user->is_admin(1);
|
||||
}
|
||||
$user->authtoken($c->param('authtoken'));
|
||||
$user->update();
|
||||
my $vars = {
|
||||
user => $user,
|
||||
};
|
||||
Litmus->template()->process("admin/edit_users/user_edited.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
} else {
|
||||
# we're here for the first time, so display the search form
|
||||
my $vars = {
|
||||
};
|
||||
print $c->header();
|
||||
Litmus->template()->process("admin/edit_users/search_users.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Zach Lipton <zach@zachlipton.com>
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Error;
|
||||
use Litmus::DB::Product;
|
||||
use Litmus::DB::TestcaseSubgroup;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Utils;
|
||||
|
||||
use CGI;
|
||||
use Time::Piece::MySQL;
|
||||
use Date::Manip;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
# for the moment, you must be an admin to enter tests:
|
||||
Litmus::Auth::requireAdmin('enter_test.cgi');
|
||||
|
||||
# if we're here for the first time, display the enter testcase form,
|
||||
# otherwise, process the results:
|
||||
|
||||
if (! $c->param('enteringTestcase')) {
|
||||
my $vars = {
|
||||
|
||||
};
|
||||
|
||||
print $c->header();
|
||||
Litmus->template()->process("enter/enter.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
} else {
|
||||
requireField('product', $c->param('product'));
|
||||
requireField('test group', $c->param('testgroup'));
|
||||
requireField('subgroup', $c->param('subgroup'));
|
||||
requireField('summary', $c->param('summary'));
|
||||
|
||||
my $newtest = Litmus::DB::Testcase->create({
|
||||
product => $c->param('product'),
|
||||
summary => $c->param('summary'),
|
||||
steps => $c->param('steps') ? $c->param('steps') : '',
|
||||
expected_results => $c->param('expectedResults') ?
|
||||
$c->param('expectedResults') : '',
|
||||
author => Litmus::Auth::getCurrentUser(),
|
||||
creation_date => &Date::Manip::UnixDate("now","%q"),
|
||||
version => 1,
|
||||
});
|
||||
|
||||
my $newtsg = Litmus::DB::TestcaseSubgroup->create({
|
||||
test => $newtest,
|
||||
subgroup => $c->param('subgroup'),
|
||||
});
|
||||
|
||||
my $vars = {
|
||||
test => $newtest,
|
||||
};
|
||||
|
||||
print $c->header();
|
||||
Litmus->template()->process("enter/enterComplete.html.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
}
|
||||
|
Before Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 332 B |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,92 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
#use Time::HiRes qw( gettimeofday tv_interval );
|
||||
#my $t0 = [gettimeofday];
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Error;
|
||||
use Litmus::DB::Testresult;
|
||||
use Litmus::FormWidget;
|
||||
|
||||
use Time::Piece::MySQL;
|
||||
|
||||
|
||||
|
||||
Litmus->init();
|
||||
|
||||
my ($criteria,$results) = Litmus::DB::Testresult->getDefaultTestResults;
|
||||
|
||||
my $products = Litmus::FormWidget->getProducts();
|
||||
my $platforms = Litmus::FormWidget->getUniquePlatforms();
|
||||
my $test_groups = Litmus::FormWidget->getTestgroups();
|
||||
my $result_statuses = Litmus::FormWidget->getResultStatuses;
|
||||
my $branches = Litmus::FormWidget->getBranches();
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
|
||||
my $vars = {
|
||||
title => 'Main Page',
|
||||
products => $products,
|
||||
platforms => $platforms,
|
||||
test_groups => $test_groups,
|
||||
result_statuses => $result_statuses,
|
||||
branches => $branches,
|
||||
limit => $Litmus::DB::Testresult::_num_results_default,
|
||||
};
|
||||
|
||||
# Only include results if we have them.
|
||||
if ($results and scalar @$results > 0) {
|
||||
$vars->{results} = $results;
|
||||
}
|
||||
|
||||
my $user = Litmus::Auth::getCurrentUser();
|
||||
if ($user) {
|
||||
$vars->{"defaultemail"} = $user;
|
||||
$vars->{"show_admin"} = $user->is_admin();
|
||||
}
|
||||
|
||||
Litmus->template()->process("index.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
#my $elapsed = tv_interval ( $t0 );
|
||||
#printf "<div id='pageload'>Page took %f seconds to load.</div>", $elapsed;
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
function adv_init() {
|
||||
advSearchFormHeight = new fx.Combo('advSearchForm', {opacity: true, height: true, duration: 1000});
|
||||
advSearchFormHeight.toggle();
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
var noDHTML = false;
|
||||
if (parseInt(navigator.appVersion) < 4) {
|
||||
window.event = 0;
|
||||
noDHTML = true;
|
||||
} else if (navigator.userAgent.indexOf("MSIE") > 0 ) {
|
||||
noDHTML = true;
|
||||
}
|
||||
if (document.body && document.body.addEventListener) {
|
||||
document.body.addEventListener("click",maybeclosepopup,false);
|
||||
}
|
||||
setTimeout('location.reload()',900000);
|
||||
|
||||
function closepopup() {
|
||||
var p = document.getElementById("popup");
|
||||
if (p && p.parentNode) {
|
||||
p.parentNode.removeChild(p);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeclosepopup(e) {
|
||||
var n = e.target;
|
||||
var close = true;
|
||||
while(close && n && (n != document)) {
|
||||
close = (n.id != "popup") && !(n.tagName && (n.tagName.toLowerCase() == "a"));
|
||||
n = n.parentNode;
|
||||
}
|
||||
if (close) closepopup();
|
||||
}
|
||||
|
||||
function who(d) {
|
||||
if (noDHTML) {
|
||||
return true;
|
||||
}
|
||||
if (typeof document.layers != 'undefined') {
|
||||
var l = document.layers['popup'];
|
||||
l.src = d.target.href;
|
||||
l.top = d.target.y - 6;
|
||||
l.left = d.target.x - 6;
|
||||
if (l.left + l.clipWidth > window.width) {
|
||||
l.left = window.width - l.clipWidth;
|
||||
}
|
||||
l.visibility="show";
|
||||
} else {
|
||||
var t = d.target;
|
||||
while (t.nodeType != 1) {
|
||||
t = t.parentNode;
|
||||
}
|
||||
closepopup()
|
||||
l = document.createElement("iframe");
|
||||
l.setAttribute("src", t.href);
|
||||
l.setAttribute("id", "popup");
|
||||
l.className = "who";
|
||||
t.appendChild(l);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function log_url(report_id) {
|
||||
return "display_report.pl?report_id=" + report_id;
|
||||
}
|
||||
|
||||
function comment(d,commentid,logfile) {
|
||||
if (noDHTML) {
|
||||
document.location = log_url(logfile);
|
||||
return false;
|
||||
}
|
||||
if (typeof document.layers != 'undefined') {
|
||||
var l = document.layers['popup'];
|
||||
l.document.write("<table border=1 cellspacing=1><tr><td>"
|
||||
+ comments[commentid] + "</tr></table>");
|
||||
l.document.close();
|
||||
l.top = d.y-10;
|
||||
var zz = d.x;
|
||||
if (zz + l.clip.right > window.innerWidth) {
|
||||
zz = (window.innerWidth-30) - l.clip.right;
|
||||
if (zz < 0) { zz = 0; }
|
||||
}
|
||||
l.left = zz;
|
||||
l.visibility="show";
|
||||
} else {
|
||||
var t = d.target;
|
||||
while (t.nodeType != 1) {
|
||||
t = t.parentNode;
|
||||
}
|
||||
closepopup()
|
||||
l = document.createElement("div");
|
||||
l.innerHTML = comments[commentid];
|
||||
l.setAttribute("id", "popup");
|
||||
l.style.position = "absolute";
|
||||
l.className = "comment";
|
||||
t.parentNode.parentNode.appendChild(l);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function log(e,buildindex,logfile) {
|
||||
var logurl = log_url(logfile);
|
||||
var commenturl = "add_comment.pl?log=" + buildtree + "/" + logfile;
|
||||
|
||||
if (noDHTML) {
|
||||
document.location = logurl;
|
||||
return false;
|
||||
}
|
||||
if (typeof document.layers != 'undefined') {
|
||||
var q = document.layers["logpopup"];
|
||||
q.top = e.target.y - 6;
|
||||
|
||||
var yy = e.target.x;
|
||||
if ( yy + q.clip.right > window.innerWidth) {
|
||||
yy = (window.innerWidth-30) - q.clip.right;
|
||||
if (yy < 0) { yy = 0; }
|
||||
}
|
||||
q.left = yy;
|
||||
q.visibility="show";
|
||||
q.document.write("<TABLE BORDER=1><TR><TD><B>"
|
||||
+ builds[buildindex] + "</B><BR>"
|
||||
+ "<A HREF=" + logurl + ">View Brief Log</A><BR>"
|
||||
+ "<A HREF=" + logurl + "&fulltext=1"+">View Full Log</A><BR>"
|
||||
+ "<A HREF=" + commenturl + ">Add a Comment</A>"
|
||||
+ "</TD></TR></TABLE>");
|
||||
q.document.close();
|
||||
} else {
|
||||
var t = e.target;
|
||||
while (t.nodeType != 1) {
|
||||
t = t.parentNode;
|
||||
}
|
||||
closepopup();
|
||||
var l = document.createElement("div");
|
||||
l.innerHTML = "<B>" + builds[buildindex] + "</B><BR>"
|
||||
+ "<A HREF=" + logurl + ">View Brief Log</A><BR>"
|
||||
+ "<A HREF=" + logurl + "&fulltext=1"+">View Full Log</A><BR>"
|
||||
+ "<A HREF=" + commenturl + ">Add a Comment</A><BR>";
|
||||
l.setAttribute("id", "popup");
|
||||
l.className = "log";
|
||||
t.parentNode.appendChild(l);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function empty_comparison_text() {
|
||||
if (document.buffalo_search.comparison_text.value == "Enter comparison text") {
|
||||
document.buffalo_search.comparison_text.value = "";
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
//the main function, call to the effect object
|
||||
function ec_init(){
|
||||
var myDivs = document.getElementsByClassName("collapsable");
|
||||
var myLinks = document.getElementsByClassName('collapse-link');
|
||||
|
||||
//then we create the effect.
|
||||
var myAccordion = new fx.Accordion(myLinks, myDivs, {opacity: true});
|
||||
myAccordion.fxa[0].toggle();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
var editedtests = new Array();
|
||||
var fields = ["product", "summary", "testgroup", "subgroup",
|
||||
"steps", "results", "admin", "formatting"];
|
||||
|
||||
function MM_findObj(n) {
|
||||
var x = document.getElementById(n);
|
||||
return x;
|
||||
}
|
||||
|
||||
function showEdit(testid) {
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
show(getField(fields[i]+"_edit",testid));
|
||||
hide(getField(fields[i]+"_text",testid));
|
||||
}
|
||||
|
||||
hide(getField("editlink", testid));
|
||||
show(getField("canceleditlink", testid));
|
||||
|
||||
document.getElementById("show_test_form").action = "show_test.cgi";
|
||||
|
||||
editedtests.push(testid);
|
||||
}
|
||||
|
||||
function findEdited() {
|
||||
MM_findObj("editingTestcases").value = editedtests.toString();
|
||||
}
|
||||
|
||||
function cancelEdit(testid) {
|
||||
for (var i=0; i<fields.length; i++) {
|
||||
hide(getField(fields[i]+"_edit",testid));
|
||||
show(getField(fields[i]+"_text",testid));
|
||||
}
|
||||
|
||||
show(getField("editlink", testid));
|
||||
hide(getField("canceleditlink", testid));
|
||||
|
||||
// remove testid from the editedtests array:
|
||||
var newarray = new Array();
|
||||
for (var i=0; i<editedtests.length; i++) {
|
||||
if (editedtests[i] != testid) {
|
||||
newarray.push(testid);
|
||||
}
|
||||
}
|
||||
editedtests=newarray;
|
||||
|
||||
document.getElementById("show_test_form").action = "process_test.cgi";
|
||||
}
|
||||
|
||||
// fields are in the format fieldname_testid
|
||||
function getField(fieldname, testid) {
|
||||
return MM_findObj(fieldname+"_"+testid);
|
||||
}
|
||||
|
||||
function show(obj) {
|
||||
if (obj) {
|
||||
obj.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
function hide(obj) {
|
||||
if (obj) {
|
||||
obj.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function tc_init() {
|
||||
if (document.getElementById("testconfig")) {
|
||||
testConfigHeight = new fx.Height('testconfig', {duration: 400});
|
||||
testConfigHeight.toggle();
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* FormPersist.js derived from CFormData.
|
||||
* See <http://devedge-temp.mozilla.org/toolbox/examples/2003/CFormData/>
|
||||
*/
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Netscape code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2003
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Bob Clary <bclary@netscape.com>
|
||||
* Bob Clary <http://bclary.com/>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function FormInit(/*HTMLFormElement */ aForm, /* String */ aQueryString)
|
||||
{
|
||||
var type;
|
||||
var options;
|
||||
|
||||
if (!aForm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aQueryString)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// empty out the form's values.
|
||||
var elements = aForm.elements;
|
||||
|
||||
for (var iElm = 0; iElm < elements.length; iElm++)
|
||||
{
|
||||
var element = elements[iElm];
|
||||
var nodeName = element.nodeName.toLowerCase();
|
||||
|
||||
switch(nodeName)
|
||||
{
|
||||
case 'input':
|
||||
type = element.type.toLowerCase();
|
||||
switch(type)
|
||||
{
|
||||
case 'text':
|
||||
element.value = '';
|
||||
break;
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
element.checked = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'textarea':
|
||||
element.value = '';
|
||||
break;
|
||||
case 'select':
|
||||
options = element.options;
|
||||
for (iOpt = 0; iOpt < options.length; iOpt++)
|
||||
{
|
||||
options[iOpt].selected = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// fill in form from the query string
|
||||
|
||||
if (aQueryString.indexOf('?') == 0);
|
||||
{
|
||||
aQueryString = aQueryString.substring(1);
|
||||
}
|
||||
|
||||
aQueryString = decodeURIComponent(aQueryString);
|
||||
aQueryString = aQueryString.replace(/[\&]amp;/g, '&');
|
||||
var parmList = aQueryString.split('&');
|
||||
|
||||
for (var iParm = 0; iParm < parmList.length; iParm++)
|
||||
{
|
||||
var parms = parmList[iParm].split('=');
|
||||
var name = parms[0];
|
||||
var value;
|
||||
|
||||
if (parms.length == 1)
|
||||
{
|
||||
value = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var tempValue = parms[1];
|
||||
value = tempValue.replace(/\+/g,' ');
|
||||
}
|
||||
|
||||
elements = aForm[name];
|
||||
|
||||
if (!elements) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof elements.nodeName != 'undefined')
|
||||
{
|
||||
elements = [elements];
|
||||
}
|
||||
|
||||
for (iElm = 0; iElm < elements.length; iElm++)
|
||||
{
|
||||
element = elements[iElm];
|
||||
nodeName = element.nodeName.toLowerCase();
|
||||
|
||||
switch(nodeName)
|
||||
{
|
||||
case 'input':
|
||||
type = element.type.toLowerCase();
|
||||
switch(type)
|
||||
{
|
||||
case 'text':
|
||||
element.value = value;
|
||||
break;
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if (element.value == value)
|
||||
{
|
||||
element.checked = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'textarea':
|
||||
element.value = value;
|
||||
break;
|
||||
case 'select':
|
||||
options = element.options;
|
||||
for (iOpt = 0; iOpt < options.length; iOpt++)
|
||||
{
|
||||
if (options[iOpt].value == value)
|
||||
{
|
||||
options[iOpt].selected = true;;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function FormDump(aForm)
|
||||
{
|
||||
var type;
|
||||
var s = '';
|
||||
var elements = aForm.elements;
|
||||
|
||||
for (var iElm = 0; iElm < elements.length; iElm++)
|
||||
{
|
||||
var element = elements[iElm];
|
||||
var nodeName = element.nodeName.toLowerCase();
|
||||
|
||||
switch(nodeName)
|
||||
{
|
||||
case 'input':
|
||||
type = element.type.toLowerCase();
|
||||
switch(type)
|
||||
{
|
||||
case 'text':
|
||||
if (element.value)
|
||||
{
|
||||
s += '&' + element.name + '=' + element.value;
|
||||
}
|
||||
break;
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if (element.checked)
|
||||
{
|
||||
s += '&' + element.name + '=' + element.value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'textarea':
|
||||
if (element.value)
|
||||
{
|
||||
s += '&' + element.name + '=' + element.value;
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
var options = element.options;
|
||||
for (iOpt = 0; iOpt < options.length; iOpt++)
|
||||
{
|
||||
if (options[iOpt].selected)
|
||||
{
|
||||
s += '&' + element.name + '=' + options[iOpt].value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
s = '?' + encodeURIComponent(s.slice(1));
|
||||
|
||||
return s;
|
||||
};
|
||||
|
||||
@@ -1,469 +0,0 @@
|
||||
var iBugNumber = "This field must be a valid, positive integer (>0). Please re-enter it now.";
|
||||
var iNumber = iBugNumber;
|
||||
var iEmail = "This field must be a valid email address (like foo@bar.com). Please re-enter it now.";
|
||||
var iBuildId = "This field must be a valid build ID, which is a non-zero string of 10 digits. Please follow the 'How do I determine the build ID?' link for more information.";
|
||||
var iPasswordMismatch = "The passwords you entered did not match.";
|
||||
var iPasswordLength = "Your password must be longer than 4 characters.";
|
||||
var defaultEmptyOK = false;
|
||||
var mPrefix = "You did not enter a value into the ";
|
||||
var mSuffix = " field. This is a required field. Please enter it now.";
|
||||
var whitespace = " \t\n\r";
|
||||
|
||||
// Check whether string s is empty.
|
||||
function isEmpty(s)
|
||||
{
|
||||
return ((s == null) || (s.length == 0));
|
||||
}
|
||||
|
||||
// Returns true if string s is empty or
|
||||
// whitespace characters only.
|
||||
function isWhitespace (s)
|
||||
{
|
||||
var i;
|
||||
|
||||
// Is s empty?
|
||||
if (isEmpty(s)) return true;
|
||||
|
||||
// Search through string's characters one by one
|
||||
// until we find a non-whitespace character.
|
||||
// When we do, return false; if we don't, return true.
|
||||
|
||||
for (i = 0; i < s.length; i++) {
|
||||
// Check that current character isn't whitespace.
|
||||
var c = s.charAt(i);
|
||||
|
||||
if (whitespace.indexOf(c) == -1) return false;
|
||||
}
|
||||
|
||||
// All characters are whitespace.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if character c is a digit
|
||||
// (0 .. 9).
|
||||
function isDigit (c)
|
||||
{
|
||||
return ((c >= "0") && (c <= "9"));
|
||||
}
|
||||
|
||||
// Notify user that required field theField is empty.
|
||||
// String s describes expected contents of theField.value.
|
||||
// Put focus in theField and return false.
|
||||
function warnEmpty (theField, s)
|
||||
{
|
||||
theField.focus();
|
||||
toggleMessage('failure',mPrefix + s + mSuffix);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notify user that contents of field theField are invalid.
|
||||
// String s describes expected contents of theField.value.
|
||||
// Put select theField, put focus in it, and return false.
|
||||
function warnInvalid (theField, s)
|
||||
{
|
||||
theField.focus();
|
||||
if (!/select/.test(theField.type)) {
|
||||
theField.select();
|
||||
}
|
||||
toggleMessage('failure',s);
|
||||
return false;
|
||||
}
|
||||
|
||||
// isEmail (STRING s [, BOOLEAN emptyOK])
|
||||
//
|
||||
// Email address must be of form a@b.c -- in other words:
|
||||
// * there must be at least one character before the @
|
||||
// * there must be at least one character before and after the .
|
||||
// * the characters @ and . are both required
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function isEmail (s)
|
||||
{
|
||||
if (isEmpty(s)) {
|
||||
if (isEmail.arguments.length == 1) {
|
||||
return defaultEmptyOK;
|
||||
} else {
|
||||
return (isEmail.arguments[1] == true);
|
||||
}
|
||||
}
|
||||
|
||||
// is s whitespace?
|
||||
if (isWhitespace(s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// there must be >= 1 character before @, so we
|
||||
// start looking at character position 1
|
||||
// (i.e. second character)
|
||||
var i = 1;
|
||||
var sLength = s.length;
|
||||
|
||||
// look for @
|
||||
while ((i < sLength) && (s.charAt(i) != "@")) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if ((i >= sLength) || (s.charAt(i) != "@")) {
|
||||
return false;
|
||||
} else {
|
||||
i += 2;
|
||||
}
|
||||
|
||||
// look for .
|
||||
while ((i < sLength) && (s.charAt(i) != ".")) {
|
||||
i++;
|
||||
}
|
||||
|
||||
// there must be at least one character after the .
|
||||
if ((i >= sLength - 1) || (s.charAt(i) != ".")) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checkString (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
|
||||
//
|
||||
// Check that string theField.value is not all whitespace.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function checkString (theField, s, emptyOK)
|
||||
{
|
||||
// Next line is needed on NN3 to avoid "undefined is not a number" error
|
||||
// in equality comparison below.
|
||||
if (checkString.arguments.length == 2) {
|
||||
emptyOK = defaultEmptyOK;
|
||||
}
|
||||
if ((emptyOK == true) && (isEmpty(theField.value))) {
|
||||
return true;
|
||||
}
|
||||
if (isWhitespace(theField.value)) {
|
||||
return warnEmpty(theField, s);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checkEmail (TEXTFIELD theField [, BOOLEAN emptyOK==false])
|
||||
//
|
||||
// Check that string theField.value is a valid Email.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function checkEmail (theField, emptyOK)
|
||||
{
|
||||
if (checkEmail.arguments.length == 1) {
|
||||
emptyOK = defaultEmptyOK;
|
||||
}
|
||||
|
||||
if ((emptyOK == true) && (isEmpty(theField.value))) {
|
||||
return true;
|
||||
} else {
|
||||
if (!isEmail(theField.value, false)) {
|
||||
return warnInvalid (theField, iEmail);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function comparePasswords(password1, password2)
|
||||
{
|
||||
if (isWhitespace(password1.value)) {
|
||||
return warnEmpty(password1, "Password");
|
||||
}
|
||||
if (isWhitespace(password2.value)) {
|
||||
return warnEmpty(password2, "Confirm Password");
|
||||
}
|
||||
if (password1.value.length < 5) {
|
||||
return warnInvalid (password1, iPasswordLength);
|
||||
}
|
||||
|
||||
if (password1.value != password2.value) {
|
||||
password1.value="";
|
||||
password2.value="";
|
||||
return warnInvalid (password1, iPasswordMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
// checkBuildId (TEXTFIELD theField [, BOOLEAN emptyOK==false])
|
||||
//
|
||||
// Check that string theField.value is a valid build id.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function checkBuildId (theField, emptyOK)
|
||||
{
|
||||
if (checkBuildId.arguments.length == 1) {
|
||||
emptyOK = defaultEmptyOK;
|
||||
}
|
||||
|
||||
if ((emptyOK == true) && (isEmpty(theField.value))) {
|
||||
return true;
|
||||
} else {
|
||||
if (!/^\d{10,10}$/.test(theField.value) || theField.value == '0000000000') {
|
||||
return warnInvalid (theField, iBuildId);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkNumber (TEXTFIELD theField [, BOOLEAN emptyOK==false])
|
||||
//
|
||||
// Check that string theField.value is a valid, positive integer.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function checkNumber (theField, emptyOK)
|
||||
{
|
||||
if (checkNumber.arguments.length == 1) {
|
||||
emptyOK = defaultEmptyOK;
|
||||
}
|
||||
|
||||
if (isPositiveInteger(theField.value,emptyOK)) {
|
||||
return true;
|
||||
} else {
|
||||
return warnInvalid (theField, iNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// isInteger (STRING s [, BOOLEAN emptyOK])
|
||||
//
|
||||
// Returns true if all characters in string s are numbers.
|
||||
//
|
||||
// Accepts non-signed integers only. Does not accept floating
|
||||
// point, exponential notation, etc.
|
||||
//
|
||||
// We don't use parseInt because that would accept a string
|
||||
// with trailing non-numeric characters.
|
||||
//
|
||||
// By default, returns defaultEmptyOK if s is empty.
|
||||
// There is an optional second argument called emptyOK.
|
||||
// emptyOK is used to override for a single function call
|
||||
// the default behavior which is specified globally by
|
||||
// defaultEmptyOK.
|
||||
// If emptyOK is false (or any value other than true),
|
||||
// the function will return false if s is empty.
|
||||
// If emptyOK is true, the function will return true if s is empty.
|
||||
//
|
||||
// EXAMPLE FUNCTION CALL: RESULT:
|
||||
// isInteger ("5") true
|
||||
// isInteger ("") defaultEmptyOK
|
||||
// isInteger ("-5") false
|
||||
// isInteger ("", true) true
|
||||
// isInteger ("", false) false
|
||||
// isInteger ("5", false) true
|
||||
function isInteger (s)
|
||||
{
|
||||
var i;
|
||||
|
||||
if (isEmpty(s))
|
||||
if (isInteger.arguments.length == 1) return defaultEmptyOK;
|
||||
else return (isInteger.arguments[1] == true);
|
||||
|
||||
// Search through string's characters one by one
|
||||
// until we find a non-numeric character.
|
||||
// When we do, return false; if we don't, return true.
|
||||
|
||||
for (i = 0; i < s.length; i++)
|
||||
{
|
||||
// Check that current character is number.
|
||||
var c = s.charAt(i);
|
||||
|
||||
if (!isDigit(c)) return false;
|
||||
}
|
||||
|
||||
// All characters are numbers.
|
||||
return true;
|
||||
}
|
||||
|
||||
// isSignedInteger (STRING s [, BOOLEAN emptyOK])
|
||||
//
|
||||
// Returns true if all characters are numbers;
|
||||
// first character is allowed to be + or - as well.
|
||||
//
|
||||
// Does not accept floating point, exponential notation, etc.
|
||||
//
|
||||
// We don't use parseInt because that would accept a string
|
||||
// with trailing non-numeric characters.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
//
|
||||
// EXAMPLE FUNCTION CALL: RESULT:
|
||||
// isSignedInteger ("5") true
|
||||
// isSignedInteger ("") defaultEmptyOK
|
||||
// isSignedInteger ("-5") true
|
||||
// isSignedInteger ("+5") true
|
||||
// isSignedInteger ("", false) false
|
||||
// isSignedInteger ("", true) true
|
||||
function isSignedInteger (s)
|
||||
{
|
||||
if (isEmpty(s))
|
||||
if (isSignedInteger.arguments.length == 1) return defaultEmptyOK;
|
||||
else return (isSignedInteger.arguments[1] == true);
|
||||
|
||||
else {
|
||||
var startPos = 0;
|
||||
var secondArg = defaultEmptyOK;
|
||||
|
||||
if (isSignedInteger.arguments.length > 1)
|
||||
secondArg = isSignedInteger.arguments[1];
|
||||
|
||||
// skip leading + or -
|
||||
if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
|
||||
startPos = 1;
|
||||
return (isInteger(s.substring(startPos, s.length), secondArg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// isPositiveInteger (STRING s [, BOOLEAN emptyOK])
|
||||
//
|
||||
// Returns true if string s is an integer > 0.
|
||||
//
|
||||
// For explanation of optional argument emptyOK,
|
||||
// see comments of function isInteger.
|
||||
function isPositiveInteger (s)
|
||||
{
|
||||
var secondArg = defaultEmptyOK;
|
||||
|
||||
if (isPositiveInteger.arguments.length > 1)
|
||||
secondArg = isPositiveInteger.arguments[1];
|
||||
|
||||
// The next line is a bit byzantine. What it means is:
|
||||
// a) s must be a signed integer, AND
|
||||
// b) one of the following must be true:
|
||||
// i) s is empty and we are supposed to return true for
|
||||
// empty strings
|
||||
// ii) this is a positive, not negative, number
|
||||
|
||||
return (isSignedInteger(s, secondArg)
|
||||
&& ( (isEmpty(s) && secondArg) || (parseInt (s) > 0) ) );
|
||||
}
|
||||
|
||||
function verifySelected(theField, fieldName) {
|
||||
if (theField.selectedIndex >= 0 &&
|
||||
theField.options[theField.selectedIndex].value != '' &&
|
||||
theField.options[theField.selectedIndex].value != '---') {
|
||||
return true;
|
||||
} else {
|
||||
return warnInvalid (theField, 'You must select an option for ' + fieldName + '. Please make a selection now.');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleMessage(msgType,msg) {
|
||||
var em = document.getElementById("message");
|
||||
if (toggleMessage.arguments.length < 1) {
|
||||
em.innerHTML="";
|
||||
em.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msgType) {
|
||||
case "loading":
|
||||
if (!msg || msg == '') {
|
||||
msg = 'Loading...';
|
||||
}
|
||||
em.innerHTML = '<div class="loading">'+msg+'</div>';
|
||||
em.style.display = 'block';
|
||||
break;
|
||||
case "info":
|
||||
em.innerHTML = '<div class="info">'+msg+'</div>';
|
||||
em.style.display = 'block';
|
||||
setTimeout('toggleMessage()',5000);
|
||||
break;
|
||||
case "success":
|
||||
em.innerHTML = '<div class="success">'+msg+'</div>';
|
||||
em.style.display = 'block';
|
||||
setTimeout('toggleMessage()',5000);
|
||||
break;
|
||||
case "failure":
|
||||
em.innerHTML = '<div class="failure">'+msg+'</div>';
|
||||
em.style.display = 'block';
|
||||
setTimeout('toggleMessage()',5000);
|
||||
break;
|
||||
case "none":
|
||||
default:
|
||||
em.innerHTML="";
|
||||
em.style.display = 'none';
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function enableForm(formid) {
|
||||
var f = document.getElementById(formid);
|
||||
var ems = f.getElementsByTagName('input');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=false;
|
||||
}
|
||||
ems = f.getElementsByTagName('select');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=false;
|
||||
}
|
||||
ems = f.getElementsByTagName('textarea');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=false;
|
||||
}
|
||||
}
|
||||
|
||||
function disableForm(formid) {
|
||||
var f = document.getElementById(formid);
|
||||
var ems = f.getElementsByTagName('input');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=true;
|
||||
}
|
||||
ems = f.getElementsByTagName('select');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=true;
|
||||
}
|
||||
ems = f.getElementsByTagName('textarea');
|
||||
for (var i in ems) {
|
||||
ems[i].disabled=true;
|
||||
}
|
||||
}
|
||||
|
||||
function changeSelectedValue(selectid, optionvalue) {
|
||||
var em = document.getElementById(selectid)
|
||||
var options = em.getElementsByTagName('option');
|
||||
var found = 0;
|
||||
for (var i=0; i<options.length; i++) {
|
||||
if (options[i].value == optionvalue) {
|
||||
options[i].selected = true;
|
||||
found=1;
|
||||
} else {
|
||||
options[i].selected = false;
|
||||
}
|
||||
}
|
||||
if (found == 0) {
|
||||
options[0].selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
function blankForm(formid) {
|
||||
var f = document.getElementById(formid);
|
||||
var ems = f.getElementsByTagName('input');
|
||||
for (var i in ems) {
|
||||
if (ems[i].type == 'submit' ||
|
||||
ems[i].value == 'Reset' ||
|
||||
ems[i].type == 'radio' ||
|
||||
ems[i].type == 'checkbox' ||
|
||||
ems[i].type == 'button') {
|
||||
continue;
|
||||
}
|
||||
ems[i].value='';
|
||||
ems[i].checked=false;
|
||||
}
|
||||
ems = f.getElementsByTagName('select');
|
||||
for (var i in ems) {
|
||||
ems[i].selectedIndex=0;
|
||||
}
|
||||
ems = f.getElementsByTagName('textarea');
|
||||
for (var i in ems) {
|
||||
ems[i].value='';
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
function toggleHelp(helpTitle,helpText) {
|
||||
var em = document.getElementById("help");
|
||||
if (toggleHelp.arguments.length < 1) {
|
||||
em.innerHTML="";
|
||||
em.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
var closeLink = '<div class="closeLink"><a name="closeHelp" onClick="toggleHelp();"><img class="chrome" src="images/close.png" />Close Help</a></div>'
|
||||
em.innerHTML = '<div class="container"><div class="title">' + helpTitle + '</div><div class="content">' + helpText + '</div>' + closeLink + '</div>';
|
||||
em.style.display = 'block';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
Copyright (c) 2005 Valerio Proietti, http://www.mad4milk.net
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
4802
mozilla/webtools/litmus/js/MochiKit/MochiKit.js
vendored
@@ -1,2 +0,0 @@
|
||||
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
||||
@@ -1,16 +0,0 @@
|
||||
function tc_init() {
|
||||
var divs = document.getElementsByClassName("testcase-content");
|
||||
allStretch = new fx.MultiFadeSize(divs, {height: true, opacity: true, duration: 400});
|
||||
|
||||
allStretch.hideAll();
|
||||
|
||||
testConfigHeight = new fx.Height('testconfig', {duration: 400});
|
||||
testConfigHeight.toggle();
|
||||
|
||||
allStretch.fxa[0].toggle();
|
||||
}
|
||||
|
||||
function confirmPartialSubmission() {
|
||||
msg = "Did you intend to only submit a single test result? (Hint: There is a 'Submit All Results' button at the bottom of the page.)";
|
||||
return confirm(msg);
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
function selects_onload() {
|
||||
load_products(getElementByClass("select_product"));
|
||||
load_testgroups(getElementByClass("select_testgroup"));
|
||||
load_subgroups(getElementByClass("select_subgroup"));
|
||||
|
||||
load_platforms(getElementByClass("select_platform"));
|
||||
load_opsyses(getElementByClass("select_opsys"));
|
||||
load_branches(getElementByClass("select_branch"));
|
||||
}
|
||||
|
||||
function load_products(selects) {
|
||||
if (!selects) { return; }
|
||||
// for each select box in selects, load in the list of products
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var productbox = selects[select];
|
||||
clearSelect(productbox);
|
||||
addNullEntry(productbox);
|
||||
for (var i=0; i<litmusconfig.length; i++) {
|
||||
var option = makeOption(litmusconfig[i]);
|
||||
productbox.add(option, null);
|
||||
// handle the default selection
|
||||
if (isDefault(document.getElementById(productbox.name+"_default"), litmusconfig[i]['id'])) {
|
||||
productbox.selectedIndex = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function load_testgroups(selects) {
|
||||
if (!selects[0]) { return; }
|
||||
// load the proper list of testgroups for the
|
||||
// currently selected product for each testgroup
|
||||
// select:
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var groupbox = selects[select];
|
||||
clearSelect(groupbox);
|
||||
addNullEntry(groupbox);
|
||||
// find the currently selected product that goes with this select
|
||||
var productbox = document.getElementById("product"+groupbox.name.substr(9));
|
||||
var productid = productbox.options[productbox.selectedIndex].value;
|
||||
var product = getProductById(productid);
|
||||
if (!product) {
|
||||
return;
|
||||
}
|
||||
// now get the list of testgroups that goes with that product:
|
||||
var testgroups = product['testgroups'];
|
||||
for (var group=0; group<testgroups.length; group++) {
|
||||
var option = makeOption(testgroups[group])
|
||||
groupbox.add(option, null);
|
||||
// handle the default selection
|
||||
if (isDefault(document.getElementById(groupbox.name+"_default"), testgroups[group]['id'])) {
|
||||
groupbox.selectedIndex = group+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function load_subgroups(selects) {
|
||||
if (!selects[0]) { return; }
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var subgroupbox = selects[select];
|
||||
clearSelect(subgroupbox);
|
||||
addNullEntry(subgroupbox);
|
||||
// find the currently selected testgroup that goes with this select
|
||||
var testgroupbox = document.getElementById("testgroup"+subgroupbox.name.substr(8));
|
||||
var testgroupid = testgroupbox.options[testgroupbox.selectedIndex].value;
|
||||
var testgroup = getTestgroupById(testgroupid);
|
||||
if (!testgroup) {
|
||||
// no testgroup set
|
||||
return;
|
||||
}
|
||||
// now get the list of subgroups that goes with that testgroup
|
||||
var subgroups = testgroup['subgroups'];
|
||||
for (var i=0; i<subgroups.length; i++) {
|
||||
var option = makeOption(subgroups[i]);
|
||||
subgroupbox.add(option, null);
|
||||
if (isDefault(document.getElementById(subgroupbox.name+"_default"), subgroups[i]['id'])) {
|
||||
subgroupbox.selectedIndex = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // wow, that was fun
|
||||
|
||||
|
||||
function load_platforms(selects) {
|
||||
if (!selects[0]) { return; }
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var platformbox = selects[select];
|
||||
clearSelect(platformbox);
|
||||
addNullEntry(platformbox);
|
||||
// find the currently selected product that goes with this select
|
||||
var productbox = document.getElementById("product"+platformbox.name.substr(8));
|
||||
var productid = productbox.options[productbox.selectedIndex].value;
|
||||
var product = getProductById(productid);
|
||||
if (!product) {
|
||||
// no product set
|
||||
return;
|
||||
}
|
||||
var platforms = product['platforms'];
|
||||
for (var i=0; i<platforms.length; i++) {
|
||||
var option = makeOption(platforms[i]);
|
||||
platformbox.add(option, null);
|
||||
if (isDefault(document.getElementById(platformbox.name+"_default"), platforms[i]['id'])) {
|
||||
platformbox.selectedIndex = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function load_branches(selects) {
|
||||
if (!selects[0]) { return; }
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var branchbox = selects[select];
|
||||
clearSelect(branchbox);
|
||||
addNullEntry(branchbox);
|
||||
// find the currently selected product that goes with this select
|
||||
var productbox = document.getElementById("product"+branchbox.name.substr(6));
|
||||
var productid = productbox.options[productbox.selectedIndex].value;
|
||||
var product = getProductById(productid);
|
||||
if (!product) {
|
||||
// no product set
|
||||
return;
|
||||
}
|
||||
var branches = product['branches'];
|
||||
for (var i=0; i<branches.length; i++) {
|
||||
var option = makeOption(branches[i]);
|
||||
branchbox.add(option, null);
|
||||
if (isDefault(document.getElementById(branchbox.name+"_default"), branches[i]['id'])) {
|
||||
branchbox.selectedIndex = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function load_opsyses(selects) {
|
||||
if (!selects[0]) { return; }
|
||||
for (var select=0; select<selects.length; select++) {
|
||||
var opsysbox = selects[select];
|
||||
clearSelect(opsysbox);
|
||||
addNullEntry(opsysbox);
|
||||
// find the currently selected platform
|
||||
var platformbox = document.getElementById("platform"+opsysbox.name.substr(5));
|
||||
var platformid = platformbox.options[platformbox.selectedIndex].value;
|
||||
var platform = getPlatformById(platformid);
|
||||
if (!platform) {
|
||||
return;
|
||||
}
|
||||
var opsyses = platform['opsyses'];
|
||||
for (var i=0; i<opsyses.length; i++) {
|
||||
var option = makeOption(opsyses[i]);
|
||||
opsysbox.add(option, null);
|
||||
if (isDefault(document.getElementById(opsysbox.name+"_default"), opsyses[i]['id'])) {
|
||||
opsysbox.selectedIndex = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function changeProduct(testid) {
|
||||
var testidflag = "";
|
||||
if (testid) { testidflag = "_"+testid; }
|
||||
|
||||
load_testgroups([document.getElementById("testgroup"+testidflag)]);
|
||||
changeTestgroup(testid);
|
||||
|
||||
load_platforms([document.getElementById("platform"+testidflag)]);
|
||||
changePlatform(testid);
|
||||
|
||||
load_branches([document.getElementById("branch"+testidflag)]);
|
||||
}
|
||||
|
||||
function changeTestgroup(testid) {
|
||||
var testidflag = "";
|
||||
if (testid) { testidflag = "_"+testid; }
|
||||
|
||||
load_subgroups([document.getElementById("subgroup"+testidflag)]);
|
||||
}
|
||||
|
||||
function changePlatform(testid) {
|
||||
var testidflag = "";
|
||||
if (testid) { testidflag = "_"+testid; }
|
||||
|
||||
load_opsyses([document.getElementById("opsys"+testidflag)]);
|
||||
}
|
||||
|
||||
function addNullEntry(select) {
|
||||
// add a blank entry to the current select
|
||||
// if possible, try to make the null entry reflect the select's
|
||||
// contents based on it's name:
|
||||
|
||||
if (select.className == 'select_product') {
|
||||
select.add(new Option("-Product-", "---", false, false), null);
|
||||
} else if (select.className == 'select_testgroup') {
|
||||
select.add(new Option("-Testgroup-", "---", false, false), null);
|
||||
} else if (select.className == 'select_subgroup') {
|
||||
select.add(new Option("-Subgroup-", "---", false, false), null);
|
||||
} else {
|
||||
select.add(new Option("---", "---", false, false), null);
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelect(select) {
|
||||
// remove all options from a select:
|
||||
while (select.options[0]) {
|
||||
select.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
function getProductById(prodid) {
|
||||
for (var i=0; i<litmusconfig.length; i++) {
|
||||
if (litmusconfig[i]['id'] == prodid) {
|
||||
return(litmusconfig[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTestgroupById(testgroupid) {
|
||||
for (var i=0; i<litmusconfig.length; i++) {
|
||||
for (var j=0; j<litmusconfig[i]['testgroups'].length; j++) {
|
||||
if (litmusconfig[i]['testgroups'][j]['id'] == testgroupid) {
|
||||
return(litmusconfig[i]['testgroups'][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getPlatformById(platformid) {
|
||||
for (var i=0; i<litmusconfig.length; i++) {
|
||||
for (var j=0; j<litmusconfig[i]['platforms'].length; j++) {
|
||||
if (litmusconfig[i]['platforms'][j]['id'] == platformid) {
|
||||
return(litmusconfig[i]['platforms'][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// pass this the <input> containing the list of possible default values
|
||||
// and the current value, returns true if the current value appears in
|
||||
// defaultInput, otherwise returns false
|
||||
function isDefault(defaultInput, curvalue) {
|
||||
if (! defaultInput) { return false; }
|
||||
var defaultarray = defaultInput.value.split(',');
|
||||
for (var i=0; i<defaultarray.length; i++) {
|
||||
if (defaultarray[i] == curvalue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function makeOption(obj) {
|
||||
return new Option(obj['name'], obj['id'], false, false)
|
||||
}
|
||||
|
||||
function getElementByClass(theClass) {
|
||||
var elements = new Array();
|
||||
var all = document.getElementsByTagName("*");
|
||||
for (var i=0; i<all.length; i++) {
|
||||
if (all[i].className == theClass) {
|
||||
elements.push(all[i]);
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
@@ -1,370 +0,0 @@
|
||||
/*
|
||||
Sort <SELECT> field script by Babvailiica
|
||||
www.babailiica.com
|
||||
version 1.3
|
||||
*/
|
||||
|
||||
function selectAll(obj) {
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select")
|
||||
return;
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
obj[i].selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
function selectNone(obj) { /* NEW added from version 1.1 */
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select")
|
||||
return;
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
obj[i].selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
function swap(obj) { /*updated from version 1.3*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var first_element = false;
|
||||
var last_element = false;
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
if (obj[i].selected) {
|
||||
if (first_element === false) {
|
||||
first_element = i;
|
||||
} else {
|
||||
last_element = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_element === false || last_element === false)
|
||||
return false;
|
||||
|
||||
var tmp = new Array((document.body.innerHTML ? obj[first_element].innerHTML : obj[first_element].text), obj[first_element].value, obj[first_element].style.color, obj[first_element].style.backgroundColor, obj[first_element].className, obj[first_element].id, obj[first_element].selected);
|
||||
if (document.body.innerHTML) obj[first_element].innerHTML = obj[last_element].innerHTML;
|
||||
else obj[first_element].text = obj[last_element].text;
|
||||
obj[first_element].value = obj[last_element].value;
|
||||
obj[first_element].style.color = obj[last_element].style.color;
|
||||
obj[first_element].style.backgroundColor = obj[last_element].style.backgroundColor;
|
||||
obj[first_element].className = obj[last_element].className;
|
||||
obj[first_element].id = obj[last_element].id;
|
||||
obj[first_element].selected = obj[last_element].selected;
|
||||
if (document.body.innerHTML) obj[last_element].innerHTML = tmp[0];
|
||||
else obj[last_element].text = tmp[0];
|
||||
obj[last_element].value = tmp[1];
|
||||
obj[last_element].style.color = tmp[2];
|
||||
obj[last_element].style.backgroundColor = tmp[3];
|
||||
obj[last_element].className = tmp[4];
|
||||
obj[last_element].id = tmp[5];
|
||||
obj[last_element].selected = tmp[6];
|
||||
}
|
||||
|
||||
function addItem(obj, text, value, index, id, classname, color, bg, selected) { /* NEW added from version 1.1 updated from version 1.2*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" || text == "")
|
||||
return;
|
||||
obj.length++;
|
||||
if (typeof index == "number" && index < obj.length-1) {
|
||||
var i = Number();
|
||||
for (i=obj.length-2; i>index-1; i--) {
|
||||
if (document.body.innerHTML) obj[i+1].innerHTML = obj[i].innerHTML;
|
||||
else obj[i+1].text = obj[i].text;
|
||||
obj[i+1].value = obj[i].value;
|
||||
obj[i+1].id = obj[i].id;
|
||||
obj[i+1].className = obj[i].className;
|
||||
obj[i+1].style.color = obj[i].style.color;
|
||||
obj[i+1].style.backgroundColor = obj[i].style.backgroundColor;
|
||||
obj[i+1].selected = obj[i].selected;
|
||||
}
|
||||
} else {
|
||||
index = obj.length - 1;
|
||||
}
|
||||
obj = obj[index];
|
||||
if (document.body.innerHTML) obj.innerHTML = text;
|
||||
else obj.text = text;
|
||||
obj.value = value;
|
||||
obj.id = id ? id : '';
|
||||
obj.className = classname ? classname : '';
|
||||
obj.style.color = color ? color : '';
|
||||
obj.style.backgroundColor = bg ? bg : '';
|
||||
obj.selected = selected
|
||||
}
|
||||
|
||||
function removeSelectedFromList(obj, index) { /* NEW added from version 1.1 */
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" || obj.length == 0)
|
||||
return;
|
||||
if (index === true) {
|
||||
for (index=obj.length-1; index>=0; index--) {
|
||||
if (obj[index].selected) {
|
||||
obj[index] = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj[((typeof index != "number") || index > (obj.length - 1) || index < 0 ? obj.length - 1 : index)] = null;
|
||||
}
|
||||
}
|
||||
|
||||
function mousewheel(obj) {
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select")
|
||||
return;
|
||||
if (obj.selectedIndex != -1) {
|
||||
if (event.wheelDelta > 0) {
|
||||
up(obj);
|
||||
} else {
|
||||
down(obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function sort2d(arrayName, element, num, cs) {
|
||||
if (num) {
|
||||
for (var i=0; i<(arrayName.length-1); i++) {
|
||||
for (var j=i+1; j<arrayName.length; j++) {
|
||||
if (parseInt(arrayName[j][element],10) < parseInt(arrayName[i][element],10)) {
|
||||
var dummy = arrayName[i];
|
||||
arrayName[i] = arrayName[j];
|
||||
arrayName[j] = dummy;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i=0; i<(arrayName.length-1); i++) {
|
||||
for (var j=i+1; j<arrayName.length; j++) {
|
||||
if (cs) {
|
||||
if (arrayName[j][element].toLowerCase() < arrayName[i][element].toLowerCase()) {
|
||||
var dummy = arrayName[i];
|
||||
arrayName[i] = arrayName[j];
|
||||
arrayName[j] = dummy;
|
||||
}
|
||||
} else {
|
||||
if (arrayName[j][element] < arrayName[i][element]) {
|
||||
var dummy = arrayName[i];
|
||||
arrayName[i] = arrayName[j];
|
||||
arrayName[j] = dummy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the list!
|
||||
by = 0 - order by text (default)
|
||||
by = 1 - order by value
|
||||
by = 2 - order by color
|
||||
by = 3 - order by background color
|
||||
by = 4 - order by class name
|
||||
by = 5 - order by id
|
||||
num = if true sorts numbers e.g. 2 before 10
|
||||
cs = casesensitive e.g. a before Z*/
|
||||
function listSort(obj, by, num, cs) { /*updated from version 1.2*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
by = (parseInt("0" + by) > 5) ? 0 : parseInt("0" + by);
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var elements = new Array();
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, (obj[i].currentStyle ? obj[i].currentStyle.color : obj[i].style.color), (obj[i].currentStyle ? obj[i].currentStyle.backgroundColor : obj[i].style.backgroundColor), obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
sort2d(elements, by, num, cs);
|
||||
for (i=0; i<obj.length; i++) {
|
||||
if (document.body.innerHTML) obj[i].innerHTML = elements[i][0];
|
||||
else obj[i].text = elements[i][0];
|
||||
obj[i].value = elements[i][1];
|
||||
obj[i].style.color = elements[i][2];
|
||||
obj[i].style.backgroundColor = elements[i][3];
|
||||
obj[i].className = elements[i][4];
|
||||
obj[i].id = elements[i][5];
|
||||
obj[i].selected = elements[i][6];
|
||||
}
|
||||
}
|
||||
|
||||
function viceVersa(obj, onlyselected) { /*updated from version 1.3*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var elements = new Array();
|
||||
for (var i=obj.length-1; i>-1; i--) {
|
||||
if (obj[i].selected || !onlyselected) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, obj[i].style.color, obj[i].style.backgroundColor, obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
}
|
||||
var a = 0;
|
||||
for (i=0; i<obj.length; i++) {
|
||||
if (obj[i].selected || !onlyselected) {
|
||||
if (document.body.innerHTML) obj[i].innerHTML = elements[a][0];
|
||||
else obj[i].text = elements[a][0];
|
||||
obj[i].value = elements[a][1];
|
||||
obj[i].style.color = elements[a][2];
|
||||
obj[i].style.backgroundColor = elements[a][3];
|
||||
obj[i].className = elements[a][4];
|
||||
obj[i].id = elements[a][5];
|
||||
obj[i].selected = elements[a][6];
|
||||
a++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function top(obj) { /*updated from version 1.2*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var elements = new Array();
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
if (obj[i].selected) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, obj[i].style.color, obj[i].style.backgroundColor, obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
}
|
||||
for (i=0; i<obj.length; i++) {
|
||||
if (!obj[i].selected) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, obj[i].style.color, obj[i].style.backgroundColor, obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
}
|
||||
for (i=0; i<obj.length; i++) {
|
||||
if (document.body.innerHTML) obj[i].innerHTML = elements[i][0];
|
||||
else obj[i].text = elements[i][0];
|
||||
obj[i].value = elements[i][1];
|
||||
obj[i].style.color = elements[i][2];
|
||||
obj[i].style.backgroundColor = elements[i][3];
|
||||
obj[i].className = elements[i][4];
|
||||
obj[i].id = elements[i][5];
|
||||
obj[i].selected = elements[i][6];
|
||||
}
|
||||
}
|
||||
|
||||
function bottom(obj) { /*updated from version 1.2*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var elements = new Array();
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
if (!obj[i].selected) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, obj[i].style.color, obj[i].style.backgroundColor, obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
}
|
||||
for (i=0; i<obj.length; i++) {
|
||||
if (obj[i].selected) {
|
||||
elements[elements.length] = new Array((document.body.innerHTML ? obj[i].innerHTML : obj[i].text), obj[i].value, obj[i].style.color, obj[i].style.backgroundColor, obj[i].className, obj[i].id, obj[i].selected);
|
||||
}
|
||||
}
|
||||
for (i=obj.length-1; i>-1; i--) {
|
||||
if (document.body.innerHTML) obj[i].innerHTML = elements[i][0];
|
||||
else obj[i].text = elements[i][0];
|
||||
obj[i].value = elements[i][1];
|
||||
obj[i].style.color = elements[i][2];
|
||||
obj[i].style.backgroundColor = elements[i][3];
|
||||
obj[i].className = elements[i][4];
|
||||
obj[i].id = elements[i][5];
|
||||
obj[i].selected = elements[i][6];
|
||||
}
|
||||
}
|
||||
|
||||
function up(obj) { /*updated from version 1.2*/
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var sel = new Array();
|
||||
for (var i=0; i<obj.length; i++) {
|
||||
if (obj[i].selected == true) {
|
||||
sel[sel.length] = i;
|
||||
}
|
||||
}
|
||||
for (i in sel) {
|
||||
if (sel[i] != 0 && obj[sel[i]-1] && !obj[sel[i]-1].selected) {
|
||||
var tmp = new Array((document.body.innerHTML ? obj[sel[i]-1].innerHTML : obj[sel[i]-1].text), obj[sel[i]-1].value, obj[sel[i]-1].id);
|
||||
if (document.body.innerHTML) obj[sel[i]-1].innerHTML = obj[sel[i]].innerHTML;
|
||||
else obj[sel[i]-1].text = obj[sel[i]].text;
|
||||
obj[sel[i]-1].value = obj[sel[i]].value;
|
||||
obj[sel[i]-1].style.color = obj[sel[i]].style.color;
|
||||
obj[sel[i]-1].style.backgroundColor = obj[sel[i]].style.backgroundColor;
|
||||
obj[sel[i]-1].className = obj[sel[i]].className;
|
||||
obj[sel[i]-1].id = obj[sel[i]].id;
|
||||
if (document.body.innerHTML) obj[sel[i]].innerHTML = tmp[0];
|
||||
else obj[sel[i]].text = tmp[0];
|
||||
obj[sel[i]].value = tmp[1];
|
||||
obj[sel[i]].style.color = tmp[2];
|
||||
obj[sel[i]].style.backgroundColor = tmp[3];
|
||||
obj[sel[i]].className = tmp[4];
|
||||
obj[sel[i]].id = tmp[5];
|
||||
obj[sel[i]-1].selected = true;
|
||||
obj[sel[i]].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function down(obj) {
|
||||
obj = (typeof obj == "string") ? document.getElementById(obj) : obj;
|
||||
if (obj.tagName.toLowerCase() != "select" && obj.length < 2)
|
||||
return false;
|
||||
var sel = new Array();
|
||||
for (var i=obj.length-1; i>-1; i--) {
|
||||
if (obj[i].selected == true) {
|
||||
sel[sel.length] = i;
|
||||
}
|
||||
}
|
||||
for (i in sel) {
|
||||
if (sel[i] != obj.length-1 && obj[sel[i]+1] && !obj[sel[i]+1].selected) {
|
||||
var tmp = new Array((document.body.innerHTML ? obj[sel[i]+1].innerHTML : obj[sel[i]+1].text), obj[sel[i]+1].value, obj[sel[i]+1].id);
|
||||
if (document.body.innerHTML) obj[sel[i]+1].innerHTML = obj[sel[i]].innerHTML;
|
||||
else obj[sel[i]+1].text = obj[sel[i]].text;
|
||||
obj[sel[i]+1].value = obj[sel[i]].value;
|
||||
obj[sel[i]+1].style.color = obj[sel[i]].style.color;
|
||||
obj[sel[i]+1].style.backgroundColor = obj[sel[i]].style.backgroundColor;
|
||||
obj[sel[i]+1].className = obj[sel[i]].className;
|
||||
obj[sel[i]+1].id = obj[sel[i]].id;
|
||||
if (document.body.innerHTML) obj[sel[i]].innerHTML = tmp[0];
|
||||
else obj[sel[i]].text = tmp[0];
|
||||
obj[sel[i]].value = tmp[1];
|
||||
obj[sel[i]].style.color = tmp[2];
|
||||
obj[sel[i]].style.backgroundColor = tmp[3];
|
||||
obj[sel[i]].className = tmp[4];
|
||||
obj[sel[i]].id = tmp[5];
|
||||
obj[sel[i]+1].selected = true;
|
||||
obj[sel[i]].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function inArray(v,a) {
|
||||
for (var i in a) {
|
||||
if (a[i] == v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function copyToList(from,to)
|
||||
{
|
||||
fromList = document.getElementById(from);
|
||||
toList = document.getElementById(to);
|
||||
if (toList.options.length > 0 && toList.options[0].value == '')
|
||||
{
|
||||
toList.options.length = 0;
|
||||
}
|
||||
var sel = false;
|
||||
for (i=0;i<fromList.options.length;i++)
|
||||
{
|
||||
var current = fromList.options[i];
|
||||
if (current.selected)
|
||||
{
|
||||
sel = true;
|
||||
if (current.value == 'temp')
|
||||
{
|
||||
alert ('You cannot move this text!');
|
||||
return;
|
||||
}
|
||||
txt = current.text;
|
||||
val = current.value;
|
||||
toList.options[toList.length] = new Option(txt,val);
|
||||
// fromList.options[i] = null;
|
||||
// i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
function MM_findObj(n, d) { //v4.01
|
||||
var p,i,x; if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
|
||||
d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
|
||||
if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
|
||||
for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
|
||||
if(!x && d.getElementById) x=d.getElementById(n); return x;
|
||||
}
|
||||
|
||||
function showsubgroup() {
|
||||
var groupselect = MM_findObj("group");
|
||||
|
||||
if (!groupselect) {
|
||||
return;
|
||||
}
|
||||
|
||||
var selnum;
|
||||
|
||||
if (groupselect.value) {
|
||||
selnum = groupselect.value;
|
||||
} else {
|
||||
for (var i=0; i<groupselect.length; i++) {
|
||||
if (groupselect[i].checked) {
|
||||
selnum = groupselect[i].value;
|
||||
}
|
||||
}
|
||||
if (!selnum) {
|
||||
groupselect[0].checked = true;
|
||||
selnum = groupselect[0].value;
|
||||
}
|
||||
}
|
||||
|
||||
// object to show
|
||||
var obj = MM_findObj("divsubgroup_"+selnum);
|
||||
|
||||
// disable all of them
|
||||
for (var i=0; i<groupselect.length; i++) {
|
||||
var gnum = groupselect[i].value;
|
||||
var disableobj = MM_findObj("divsubgroup_"+gnum);
|
||||
disableobj.style.display = "none";
|
||||
}
|
||||
MM_findObj("divsubgroup_null").style.display = "none";
|
||||
|
||||
var num_subgroups_enabled = 0;
|
||||
var subgroupselect = MM_findObj("subgroup_"+selnum);
|
||||
|
||||
if (!subgroupselect) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (subgroupselect.value) {
|
||||
num_subgroups_enabled=1;
|
||||
} else {
|
||||
for (var i=0; i<subgroupselect.length; i++) {
|
||||
if (!subgroupselect[i].disabled) {
|
||||
num_subgroups_enabled++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.style.display = "";
|
||||
|
||||
if (num_subgroups_enabled == 0) {
|
||||
MM_findObj("Submit").disabled = true;
|
||||
} else {
|
||||
MM_findObj("Submit").disabled = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function group_init() {
|
||||
testConfigHeight = new fx.Height('testconfig', {duration: 400});
|
||||
testConfigHeight.hide();
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
json.js
|
||||
2006-04-28
|
||||
|
||||
This file adds these methods to JavaScript:
|
||||
|
||||
object.toJSONString()
|
||||
|
||||
This method produces a JSON text from an object. The
|
||||
object must not contain any cyclical references.
|
||||
|
||||
array.toJSONString()
|
||||
|
||||
This method produces a JSON text from an array. The
|
||||
array must not contain any cyclical references.
|
||||
|
||||
string.parseJSON()
|
||||
|
||||
This method parses a JSON text to produce an object or
|
||||
array. It will return false if there is an error.
|
||||
*/
|
||||
(function () {
|
||||
var m = {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
s = {
|
||||
array: function (x) {
|
||||
var a = ['['], b, f, i, l = x.length, v;
|
||||
for (i = 0; i < l; i += 1) {
|
||||
v = x[i];
|
||||
f = s[typeof v];
|
||||
if (f) {
|
||||
v = f(v);
|
||||
if (typeof v == 'string') {
|
||||
if (b) {
|
||||
a[a.length] = ',';
|
||||
}
|
||||
a[a.length] = v;
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
a[a.length] = ']';
|
||||
return a.join('');
|
||||
},
|
||||
'boolean': function (x) {
|
||||
return String(x);
|
||||
},
|
||||
'null': function (x) {
|
||||
return "null";
|
||||
},
|
||||
number: function (x) {
|
||||
return isFinite(x) ? String(x) : 'null';
|
||||
},
|
||||
object: function (x) {
|
||||
if (x) {
|
||||
if (x instanceof Array) {
|
||||
return s.array(x);
|
||||
}
|
||||
var a = ['{'], b, f, i, v;
|
||||
for (i in x) {
|
||||
v = x[i];
|
||||
f = s[typeof v];
|
||||
if (f) {
|
||||
v = f(v);
|
||||
if (typeof v == 'string') {
|
||||
if (b) {
|
||||
a[a.length] = ',';
|
||||
}
|
||||
a.push(s.string(i), ':', v);
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
a[a.length] = '}';
|
||||
return a.join('');
|
||||
}
|
||||
return 'null';
|
||||
},
|
||||
string: function (x) {
|
||||
if (/["\\\x00-\x1f]/.test(x)) {
|
||||
x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
|
||||
var c = m[b];
|
||||
if (c) {
|
||||
return c;
|
||||
}
|
||||
c = b.charCodeAt();
|
||||
return '\\u00' +
|
||||
Math.floor(c / 16).toString(16) +
|
||||
(c % 16).toString(16);
|
||||
});
|
||||
}
|
||||
return '"' + x + '"';
|
||||
}
|
||||
};
|
||||
|
||||
Object.prototype.toJSONString = function () {
|
||||
return s.object(this);
|
||||
};
|
||||
|
||||
Array.prototype.toJSONString = function () {
|
||||
return s.array(this);
|
||||
};
|
||||
})();
|
||||
|
||||
String.prototype.parseJSON = function () {
|
||||
try {
|
||||
return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
|
||||
this.replace(/"(\\.|[^"\\])*"/g, ''))) &&
|
||||
eval('(' + this + ')');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
function fetchJSON(url,callbackFunction) {
|
||||
var d = loadJSONDoc(url);
|
||||
d.addBoth(function (res) {
|
||||
d.deferred = null;
|
||||
toggleMessage('none');
|
||||
return res;
|
||||
});
|
||||
d.addCallback(callbackFunction);
|
||||
// if anything goes wrong, except for a simple cancellation,
|
||||
// then log the error and show the logger
|
||||
d.addErrback(function (err) {
|
||||
if (err instanceof CancelledError) {
|
||||
return;
|
||||
}
|
||||
logError(err);
|
||||
logger.debuggingBookmarklet();
|
||||
});
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
moo.fx, simple effects library built with prototype.js (http://prototype.conio.net).
|
||||
by Valerio Proietti (http://mad4milk.net) MIT-style LICENSE.
|
||||
for more info (http://moofx.mad4milk.net).
|
||||
Sunday, March 05, 2006
|
||||
v 1.2.3
|
||||
*/
|
||||
|
||||
var fx = new Object();
|
||||
//base
|
||||
fx.Base = function(){};
|
||||
fx.Base.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
duration: 500,
|
||||
onComplete: '',
|
||||
transition: fx.sinoidal
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
},
|
||||
|
||||
step: function() {
|
||||
var time = (new Date).getTime();
|
||||
if (time >= this.options.duration+this.startTime) {
|
||||
this.now = this.to;
|
||||
clearInterval (this.timer);
|
||||
this.timer = null;
|
||||
if (this.options.onComplete) setTimeout(this.options.onComplete.bind(this), 10);
|
||||
}
|
||||
else {
|
||||
var Tpos = (time - this.startTime) / (this.options.duration);
|
||||
this.now = this.options.transition(Tpos) * (this.to-this.from) + this.from;
|
||||
}
|
||||
this.increase();
|
||||
},
|
||||
|
||||
custom: function(from, to) {
|
||||
if (this.timer != null) return;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.startTime = (new Date).getTime();
|
||||
this.timer = setInterval (this.step.bind(this), 13);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.now = 0;
|
||||
this.increase();
|
||||
},
|
||||
|
||||
clearTimer: function() {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
//stretchers
|
||||
fx.Layout = Class.create();
|
||||
fx.Layout.prototype = Object.extend(new fx.Base(), {
|
||||
initialize: function(el, options) {
|
||||
this.el = $(el);
|
||||
this.el.style.overflow = "hidden";
|
||||
this.iniWidth = this.el.offsetWidth;
|
||||
this.iniHeight = this.el.offsetHeight;
|
||||
this.setOptions(options);
|
||||
}
|
||||
});
|
||||
|
||||
fx.Height = Class.create();
|
||||
Object.extend(Object.extend(fx.Height.prototype, fx.Layout.prototype), {
|
||||
increase: function() {
|
||||
this.el.style.height = this.now + "px";
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.el.offsetHeight > 0) this.custom(this.el.offsetHeight, 0);
|
||||
else this.custom(0, this.el.scrollHeight);
|
||||
}
|
||||
});
|
||||
|
||||
fx.Width = Class.create();
|
||||
Object.extend(Object.extend(fx.Width.prototype, fx.Layout.prototype), {
|
||||
increase: function() {
|
||||
this.el.style.width = this.now + "px";
|
||||
},
|
||||
|
||||
toggle: function(){
|
||||
if (this.el.offsetWidth > 0) this.custom(this.el.offsetWidth, 0);
|
||||
else this.custom(0, this.iniWidth);
|
||||
}
|
||||
});
|
||||
|
||||
//fader
|
||||
fx.Opacity = Class.create();
|
||||
fx.Opacity.prototype = Object.extend(new fx.Base(), {
|
||||
initialize: function(el, options) {
|
||||
this.el = $(el);
|
||||
this.now = 1;
|
||||
this.increase();
|
||||
this.setOptions(options);
|
||||
},
|
||||
|
||||
increase: function() {
|
||||
if (this.now == 1 && (/Firefox/.test(navigator.userAgent))) this.now = 0.9999;
|
||||
this.setOpacity(this.now);
|
||||
},
|
||||
|
||||
setOpacity: function(opacity) {
|
||||
if (opacity == 0 && this.el.style.visibility != "hidden") this.el.style.visibility = "hidden";
|
||||
else if (this.el.style.visibility != "visible") this.el.style.visibility = "visible";
|
||||
if (window.ActiveXObject) this.el.style.filter = "alpha(opacity=" + opacity*100 + ")";
|
||||
this.el.style.opacity = opacity;
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.now > 0) this.custom(1, 0);
|
||||
else this.custom(0, 1);
|
||||
}
|
||||
});
|
||||
|
||||
//transitions
|
||||
fx.sinoidal = function(pos){
|
||||
return ((-Math.cos(pos*Math.PI)/2) + 0.5);
|
||||
//this transition is from script.aculo.us
|
||||
}
|
||||
fx.linear = function(pos){
|
||||
return pos;
|
||||
}
|
||||
fx.cubic = function(pos){
|
||||
return Math.pow(pos, 3);
|
||||
}
|
||||
fx.circ = function(pos){
|
||||
return Math.sqrt(pos);
|
||||
}
|
||||
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
moo.fx pack, effects extensions for moo.fx.
|
||||
by Valerio Proietti (http://mad4milk.net) MIT-style LICENSE
|
||||
for more info visit (http://moofx.mad4milk.net).
|
||||
Friday, April 14, 2006
|
||||
v 1.2.4
|
||||
*/
|
||||
|
||||
//smooth scroll
|
||||
fx.Scroll = Class.create();
|
||||
fx.Scroll.prototype = Object.extend(new fx.Base(), {
|
||||
initialize: function(options) {
|
||||
this.setOptions(options);
|
||||
},
|
||||
|
||||
scrollTo: function(el){
|
||||
var dest = Position.cumulativeOffset($(el))[1];
|
||||
var client = window.innerHeight || document.documentElement.clientHeight;
|
||||
var full = document.documentElement.scrollHeight;
|
||||
var top = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop;
|
||||
if (dest+client > full) this.custom(top, dest - client + (full-dest));
|
||||
else this.custom(top, dest);
|
||||
},
|
||||
|
||||
increase: function(){
|
||||
window.scrollTo(0, this.now);
|
||||
}
|
||||
});
|
||||
|
||||
//text size modify, now works with pixels too.
|
||||
fx.Text = Class.create();
|
||||
fx.Text.prototype = Object.extend(new fx.Base(), {
|
||||
initialize: function(el, options) {
|
||||
this.el = $(el);
|
||||
this.setOptions(options);
|
||||
if (!this.options.unit) this.options.unit = "em";
|
||||
},
|
||||
|
||||
increase: function() {
|
||||
this.el.style.fontSize = this.now + this.options.unit;
|
||||
}
|
||||
});
|
||||
|
||||
//composition effect: widht/height/opacity
|
||||
fx.Combo = Class.create();
|
||||
fx.Combo.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
opacity: true,
|
||||
height: true,
|
||||
width: false
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
},
|
||||
|
||||
initialize: function(el, options) {
|
||||
this.el = $(el);
|
||||
this.setOptions(options);
|
||||
if (this.options.opacity) {
|
||||
this.o = new fx.Opacity(el, options);
|
||||
options.onComplete = null;
|
||||
}
|
||||
if (this.options.height) {
|
||||
this.h = new fx.Height(el, options);
|
||||
options.onComplete = null;
|
||||
}
|
||||
if (this.options.width) this.w = new fx.Width(el, options);
|
||||
},
|
||||
|
||||
toggle: function() { this.checkExec('toggle'); },
|
||||
|
||||
hide: function(){ this.checkExec('hide'); },
|
||||
|
||||
clearTimer: function(){ this.checkExec('clearTimer'); },
|
||||
|
||||
checkExec: function(func){
|
||||
if (this.o) this.o[func]();
|
||||
if (this.h) this.h[func]();
|
||||
if (this.w) this.w[func]();
|
||||
},
|
||||
|
||||
//only if width+height
|
||||
resizeTo: function(hto, wto) {
|
||||
if (this.h && this.w) {
|
||||
this.h.custom(this.el.offsetHeight, this.el.offsetHeight + hto);
|
||||
this.w.custom(this.el.offsetWidth, this.el.offsetWidth + wto);
|
||||
}
|
||||
},
|
||||
|
||||
customSize: function(hto, wto) {
|
||||
if (this.h && this.w) {
|
||||
this.h.custom(this.el.offsetHeight, hto);
|
||||
this.w.custom(this.el.offsetWidth, wto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fx.MultiFadeSize = Class.create();
|
||||
fx.MultiFadeSize.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
delay: 100,
|
||||
opacity: false
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
},
|
||||
|
||||
initialize: function(elements, options) {
|
||||
this.elements = elements;
|
||||
this.setOptions(options);
|
||||
var options = options || '';
|
||||
this.fxa = [];
|
||||
if (options && options.onComplete) options.onFinish = options.onComplete;
|
||||
elements.each(function(el, i){
|
||||
options.onComplete = function(){
|
||||
if (el.offsetHeight > 0) el.style.height = '1%';
|
||||
if (options.onFinish) options.onFinish(el);
|
||||
}
|
||||
this.fxa[i] = new fx.Combo(el, options);
|
||||
this.fxa[i].hide();
|
||||
}.bind(this));
|
||||
|
||||
},
|
||||
|
||||
showThisHideOpen: function(toShow){
|
||||
this.elements.each(function(el, j){
|
||||
if (el.offsetHeight > 0 && el != toShow) this.clearAndToggle(el, j);
|
||||
if (el == toShow && toShow.offsetHeight == 0) setTimeout(function(){this.clearAndToggle(toShow, j);}.bind(this), this.options.delay);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
clearAndToggle: function(el, i){
|
||||
this.fxa[i].clearTimer();
|
||||
this.fxa[i].toggle();
|
||||
},
|
||||
|
||||
showAll: function() {
|
||||
for (i=0;i<this.elements.length;i++){
|
||||
if (this.elements[i].offsetHeight == 0) {
|
||||
this.clearAndToggle(this.elements[i], i)
|
||||
}
|
||||
}
|
||||
showAll=1;
|
||||
},
|
||||
|
||||
hideAll: function() {
|
||||
for (i=0;i<this.elements.length;i++){
|
||||
if (this.elements[i].offsetHeight > 0) {
|
||||
this.clearAndToggle(this.elements[i], i)
|
||||
}
|
||||
}
|
||||
showAll=1;
|
||||
},
|
||||
|
||||
toggle: function(el){
|
||||
el = $(el);
|
||||
for (i=0;i<this.elements.length;i++){
|
||||
if (this.elements[i] == el) {
|
||||
this.clearAndToggle(this.elements[i], i)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
hide: function(el){
|
||||
el = $(el);
|
||||
for (i=0;i<this.elements.length;i++){
|
||||
if (this.elements[i] == el) {
|
||||
this.fxa[i].hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fx.Accordion = Class.create();
|
||||
fx.Accordion.prototype = {
|
||||
setOptions: function(options) {
|
||||
this.options = {
|
||||
delay: 100,
|
||||
opacity: false
|
||||
}
|
||||
Object.extend(this.options, options || {});
|
||||
},
|
||||
|
||||
initialize: function(togglers, elements, options) {
|
||||
this.elements = elements;
|
||||
this.setOptions(options);
|
||||
var options = options || '';
|
||||
this.fxa = [];
|
||||
if (options && options.onComplete) options.onFinish = options.onComplete;
|
||||
elements.each(function(el, i){
|
||||
options.onComplete = function(){
|
||||
if (el.offsetHeight > 0) el.style.height = '1%';
|
||||
if (options.onFinish) options.onFinish(el);
|
||||
}
|
||||
this.fxa[i] = new fx.Combo(el, options);
|
||||
this.fxa[i].hide();
|
||||
}.bind(this));
|
||||
|
||||
togglers.each(function(tog, i){
|
||||
if (typeof tog.onclick == 'function') var exClick = tog.onclick;
|
||||
tog.onclick = function(){
|
||||
if (exClick) exClick();
|
||||
this.showThisHideOpen(elements[i]);
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
showThisHideOpen: function(toShow){
|
||||
this.elements.each(function(el, j){
|
||||
if (el.offsetHeight > 0 && el != toShow) this.clearAndToggle(el, j);
|
||||
if (el == toShow && toShow.offsetHeight == 0) setTimeout(function(){this.clearAndToggle(toShow, j);}.bind(this), this.options.delay);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
clearAndToggle: function(el, i){
|
||||
this.fxa[i].clearTimer();
|
||||
this.fxa[i].toggle();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var Remember = new Object();
|
||||
Remember = function(){};
|
||||
Remember.prototype = {
|
||||
initialize: function(el, options){
|
||||
this.el = $(el);
|
||||
this.days = 365;
|
||||
this.options = options;
|
||||
this.effect();
|
||||
var cookie = this.readCookie();
|
||||
if (cookie) {
|
||||
this.fx.now = cookie;
|
||||
this.fx.increase();
|
||||
}
|
||||
},
|
||||
|
||||
//cookie functions based on code by Peter-Paul Koch
|
||||
setCookie: function(value) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(this.days*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
document.cookie = this.el+this.el.id+this.prefix+"="+value+expires+"; path=/";
|
||||
},
|
||||
|
||||
readCookie: function() {
|
||||
var nameEQ = this.el+this.el.id+this.prefix + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i=0;c=ca[i];i++) {
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
custom: function(from, to){
|
||||
if (this.fx.now != to) {
|
||||
this.setCookie(to);
|
||||
this.fx.custom(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fx.RememberHeight = Class.create();
|
||||
fx.RememberHeight.prototype = Object.extend(new Remember(), {
|
||||
effect: function(){
|
||||
this.fx = new fx.Height(this.el, this.options);
|
||||
this.prefix = 'height';
|
||||
},
|
||||
|
||||
toggle: function(){
|
||||
if (this.el.offsetHeight == 0) this.setCookie(this.el.scrollHeight);
|
||||
else this.setCookie(0);
|
||||
this.fx.toggle();
|
||||
},
|
||||
|
||||
resize: function(to){
|
||||
this.setCookie(this.el.offsetHeight+to);
|
||||
this.fx.custom(this.el.offsetHeight,this.el.offsetHeight+to);
|
||||
},
|
||||
|
||||
hide: function(){
|
||||
if (!this.readCookie()) {
|
||||
this.fx.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fx.RememberText = Class.create();
|
||||
fx.RememberText.prototype = Object.extend(new Remember(), {
|
||||
effect: function(){
|
||||
this.fx = new fx.Text(this.el, this.options);
|
||||
this.prefix = 'text';
|
||||
}
|
||||
});
|
||||
|
||||
//useful for-replacement
|
||||
Array.prototype.iterate = function(func){
|
||||
for(var i=0;i<this.length;i++) func(this[i], i);
|
||||
}
|
||||
if (!Array.prototype.each) Array.prototype.each = Array.prototype.iterate;
|
||||
|
||||
//Easing Equations (c) 2003 Robert Penner, all rights reserved.
|
||||
//This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html.
|
||||
|
||||
//expo
|
||||
fx.expoIn = function(pos){
|
||||
return Math.pow(2, 10 * (pos - 1));
|
||||
}
|
||||
fx.expoOut = function(pos){
|
||||
return (-Math.pow(2, -10 * pos) + 1);
|
||||
}
|
||||
|
||||
//quad
|
||||
fx.quadIn = function(pos){
|
||||
return Math.pow(pos, 2);
|
||||
}
|
||||
fx.quadOut = function(pos){
|
||||
return -(pos)*(pos-2);
|
||||
}
|
||||
|
||||
//circ
|
||||
fx.circOut = function(pos){
|
||||
return Math.sqrt(1 - Math.pow(pos-1,2));
|
||||
}
|
||||
fx.circIn = function(pos){
|
||||
return -(Math.sqrt(1 - Math.pow(pos, 2)) - 1);
|
||||
}
|
||||
|
||||
//back
|
||||
fx.backIn = function(pos){
|
||||
return (pos)*pos*((2.7)*pos - 1.7);
|
||||
}
|
||||
fx.backOut = function(pos){
|
||||
return ((pos-1)*(pos-1)*((2.7)*(pos-1) + 1.7) + 1);
|
||||
}
|
||||
|
||||
//sine
|
||||
fx.sineOut = function(pos){
|
||||
return Math.sin(pos * (Math.PI/2));
|
||||
}
|
||||
fx.sineIn = function(pos){
|
||||
return -Math.cos(pos * (Math.PI/2)) + 1;
|
||||
}
|
||||
fx.sineInOut = function(pos){
|
||||
return -(Math.cos(Math.PI*pos) - 1)/2;
|
||||
}
|
||||
132
mozilla/webtools/litmus/js/prototype.lite.js
vendored
@@ -1,132 +0,0 @@
|
||||
/* Prototype JavaScript framework
|
||||
* (c) 2005 Sam Stephenson <sam@conio.net>
|
||||
* Prototype is freely distributable under the terms of an MIT-style license.
|
||||
* For details, see the Prototype web site: http://prototype.conio.net/
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
//note: modified & stripped down version of prototype, to be used with moo.fx by mad4milk (http://moofx.mad4milk.net).
|
||||
|
||||
var Class = {
|
||||
create: function() {
|
||||
return function() {
|
||||
this.initialize.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.extend = function(destination, source) {
|
||||
for (property in source) destination[property] = source[property];
|
||||
return destination;
|
||||
}
|
||||
|
||||
Function.prototype.bind = function(object) {
|
||||
var __method = this;
|
||||
return function() {
|
||||
return __method.apply(object, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
Function.prototype.bindAsEventListener = function(object) {
|
||||
var __method = this;
|
||||
return function(event) {
|
||||
__method.call(object, event || window.event);
|
||||
}
|
||||
}
|
||||
|
||||
function $() {
|
||||
if (arguments.length == 1) return get$(arguments[0]);
|
||||
var elements = [];
|
||||
$c(arguments).each(function(el){
|
||||
elements.push(get$(el));
|
||||
});
|
||||
return elements;
|
||||
|
||||
function get$(el){
|
||||
if (typeof el == 'string') el = document.getElementById(el);
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.Element) var Element = new Object();
|
||||
|
||||
Object.extend(Element, {
|
||||
remove: function(element) {
|
||||
element = $(element);
|
||||
element.parentNode.removeChild(element);
|
||||
},
|
||||
|
||||
hasClassName: function(element, className) {
|
||||
element = $(element);
|
||||
if (!element) return;
|
||||
var hasClass = false;
|
||||
element.className.split(' ').each(function(cn){
|
||||
if (cn == className) hasClass = true;
|
||||
});
|
||||
return hasClass;
|
||||
},
|
||||
|
||||
addClassName: function(element, className) {
|
||||
element = $(element);
|
||||
Element.removeClassName(element, className);
|
||||
element.className += ' ' + className;
|
||||
},
|
||||
|
||||
removeClassName: function(element, className) {
|
||||
element = $(element);
|
||||
if (!element) return;
|
||||
var newClassName = '';
|
||||
element.className.split(' ').each(function(cn, i){
|
||||
if (cn != className){
|
||||
if (i > 0) newClassName += ' ';
|
||||
newClassName += cn;
|
||||
}
|
||||
});
|
||||
element.className = newClassName;
|
||||
},
|
||||
|
||||
cleanWhitespace: function(element) {
|
||||
element = $(element);
|
||||
$c(element.childNodes).each(function(node){
|
||||
if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) Element.remove(node);
|
||||
});
|
||||
},
|
||||
|
||||
find: function(element, what) {
|
||||
element = $(element)[what];
|
||||
while (element.nodeType != 1) element = element[what];
|
||||
return element;
|
||||
}
|
||||
});
|
||||
|
||||
var Position = {
|
||||
cumulativeOffset: function(element) {
|
||||
var valueT = 0, valueL = 0;
|
||||
do {
|
||||
valueT += element.offsetTop || 0;
|
||||
valueL += element.offsetLeft || 0;
|
||||
element = element.offsetParent;
|
||||
} while (element);
|
||||
return [valueL, valueT];
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementsByClassName = function(className) {
|
||||
var children = document.getElementsByTagName('*') || document.all;
|
||||
var elements = [];
|
||||
$c(children).each(function(child){
|
||||
if (Element.hasClassName(child, className)) elements.push(child);
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
|
||||
//useful array functions
|
||||
Array.prototype.iterate = function(func){
|
||||
for(var i=0;i<this.length;i++) func(this[i], i);
|
||||
}
|
||||
if (!Array.prototype.each) Array.prototype.each = Array.prototype.iterate;
|
||||
|
||||
function $c(array){
|
||||
var nArray = [];
|
||||
for (var i=0;i<array.length;i++) nArray.push(array[i]);
|
||||
return nArray;
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Error;
|
||||
use Text::Markdown;
|
||||
use JSON;
|
||||
|
||||
use CGI;
|
||||
use Date::Manip;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header('text/plain');
|
||||
|
||||
if ($c->param("testcase_id")) {
|
||||
my $testcase_id = $c->param("testcase_id");
|
||||
my $testcase = Litmus::DB::Testcase->retrieve($testcase_id);
|
||||
my @testgroups = Litmus::DB::Testgroup->search_EnabledByTestcase($testcase_id);
|
||||
my @subgroups = Litmus::DB::Subgroup->search_EnabledByTestcase($testcase_id);
|
||||
$testcase->{'testgroups'} = \@testgroups;
|
||||
$testcase->{'subgroups'} = \@subgroups;
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
|
||||
# apply markdown formatting to the steps and expected results:
|
||||
$testcase->{'steps_formatted'} = Text::Markdown::markdown($testcase->steps());
|
||||
$testcase->{'expected_results_formatted'} = Text::Markdown::markdown($testcase->expected_results());
|
||||
|
||||
my $js = $json->objToJson($testcase);
|
||||
print $js;
|
||||
} elsif ($c->param("subgroup_id")) {
|
||||
my $subgroup_id = $c->param("subgroup_id");
|
||||
my $subgroup = Litmus::DB::Subgroup->retrieve($subgroup_id);
|
||||
my @testgroups = Litmus::DB::Testgroup->search_EnabledBySubgroup($subgroup_id);
|
||||
my @testcases = Litmus::DB::Testcase->search_EnabledBySubgroup($subgroup_id);
|
||||
$subgroup->{'testgroups'} = \@testgroups;
|
||||
$subgroup->{'testcases'} = \@testcases;
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($subgroup);
|
||||
print $js;
|
||||
} elsif ($c->param("testgroup_id")) {
|
||||
my $testgroup_id = $c->param("testgroup_id");
|
||||
my $testgroup = Litmus::DB::Testgroup->retrieve($testgroup_id);
|
||||
my @subgroups = Litmus::DB::Subgroup->search_EnabledByTestgroup($testgroup_id);
|
||||
$testgroup->{'subgroups'} = \@subgroups;
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($testgroup);
|
||||
print $js;
|
||||
} elsif ($c->param("product_id")) {
|
||||
my $product_id = $c->param("product_id");
|
||||
my $product = Litmus::DB::Product->retrieve($product_id);
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($product);
|
||||
print $js;
|
||||
} elsif ($c->param("platform_id")) {
|
||||
my $platform_id = $c->param("platform_id");
|
||||
my $platform = Litmus::DB::Platform->retrieve($platform_id);
|
||||
my @products = Litmus::DB::Product->search_ByPlatform($platform_id);
|
||||
$platform->{'products'} = \@products;
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($platform);
|
||||
print $js;
|
||||
} elsif ($c->param("opsys_id")) {
|
||||
my $opsys_id = $c->param("opsys_id");
|
||||
my $opsys = Litmus::DB::Opsys->retrieve($opsys_id);
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($opsys);
|
||||
print $js;
|
||||
} elsif ($c->param("branch_id")) {
|
||||
my $branch_id = $c->param("branch_id");
|
||||
my $branch = Litmus::DB::Branch->retrieve($branch_id);
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $js = $json->objToJson($branch);
|
||||
print $js;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
|
||||
Litmus->init();
|
||||
|
||||
use CGI;
|
||||
|
||||
|
||||
my $title = "Log in";
|
||||
|
||||
Litmus::Auth::requireLogin("index.cgi");
|
||||
|
||||
exit;
|
||||
@@ -1,56 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
|
||||
|
||||
|
||||
Litmus->init();
|
||||
|
||||
my $title = "Log out";
|
||||
|
||||
Litmus::Auth::logout();
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
|
||||
my $vars = {
|
||||
title => $title,
|
||||
};
|
||||
|
||||
Litmus->template()->process("logout.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
|
||||
|
||||
exit;
|
||||
@@ -1,352 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
$|++;
|
||||
|
||||
#use Time::HiRes qw( gettimeofday tv_interval );
|
||||
#my $t0 = [gettimeofday];
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Cache;
|
||||
use Litmus::Error;
|
||||
use Litmus::FormWidget;
|
||||
|
||||
use CGI;
|
||||
use Time::Piece::MySQL;
|
||||
|
||||
|
||||
|
||||
Litmus->init();
|
||||
Litmus::Auth::requireAdmin("edit_categories.cgi");
|
||||
|
||||
my $c = Litmus->cgi();
|
||||
print $c->header();
|
||||
|
||||
my $message;
|
||||
my $status;
|
||||
my $rv;
|
||||
my $rebuild_cache = 0;
|
||||
my $defaults;
|
||||
|
||||
if ($c->param) {
|
||||
# Process product changes.
|
||||
if ($c->param("delete_product_button") and
|
||||
$c->param("product_id")) {
|
||||
my $product_id = $c->param("product_id");
|
||||
my $product = Litmus::DB::Product->retrieve($product_id);
|
||||
if ($product) {
|
||||
$rv = $product->delete_with_refs();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Product ID# $product_id deleted successfully.";
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to delete Product ID# $product_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Product ID# $product_id does not exist. (Already deleted?)";
|
||||
}
|
||||
} elsif ($c->param("edit_product_form_mode")) {
|
||||
my $enabled = $c->param('edit_product_form_enabled') ? 1 : 0;
|
||||
if ($c->param("edit_product_form_mode") eq "add") {
|
||||
my %hash = (
|
||||
name => $c->param('edit_product_form_name'),
|
||||
iconpath => $c->param('edit_product_form_iconpath'),
|
||||
enabled => $enabled,
|
||||
);
|
||||
my $new_product =
|
||||
Litmus::DB::Product->create(\%hash);
|
||||
if ($new_product) {
|
||||
$status = "success";
|
||||
$message = "Product added successfully. New product ID# is " . $new_product->product_id;
|
||||
$defaults->{'product_id'} = $new_product->product_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to add product.";
|
||||
}
|
||||
} elsif ($c->param("edit_product_form_mode") eq "edit") {
|
||||
my $product_id = $c->param("edit_product_form_product_id");
|
||||
my $product = Litmus::DB::Product->retrieve($product_id);
|
||||
if ($product) {
|
||||
$product->name($c->param('edit_product_form_name'));
|
||||
$product->iconpath($c->param('edit_product_form_iconpath') ? $c->param('edit_product_form_iconpath') : '');
|
||||
$product->enabled($enabled);
|
||||
$rv = $product->update();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Product ID# $product_id updated successfully.";
|
||||
$defaults->{'product_id'} = $product_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to update product ID# $product_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Product ID# $product_id not found.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Process platform changes.
|
||||
if ($c->param("delete_platform_button") and
|
||||
$c->param("platform_id")) {
|
||||
my $platform_id = $c->param("platform_id");
|
||||
my $platform = Litmus::DB::Platform->retrieve($platform_id);
|
||||
if ($platform) {
|
||||
$rv = $platform->delete_with_refs();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Platform ID# $platform_id deleted successfully.";
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to delete Platform ID# $platform_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Platform ID# $platform_id does not exist. (Already deleted?)";
|
||||
}
|
||||
} elsif ($c->param("edit_platform_form_mode")) {
|
||||
if ($c->param("edit_platform_form_mode") eq "add") {
|
||||
my %hash = (
|
||||
name => $c->param('edit_platform_form_name'),
|
||||
iconpath => $c->param('edit_platform_form_iconpath'),
|
||||
detect_regexp => $c->param('edit_platform_form_detect_regexp'),
|
||||
);
|
||||
my $new_platform =
|
||||
Litmus::DB::Platform->create(\%hash);
|
||||
if ($new_platform) {
|
||||
my @selected_products = $c->param("edit_platform_form_platform_products");
|
||||
$new_platform->update_products(\@selected_products);
|
||||
$status = "success";
|
||||
$message = "Platform added successfully. New platform ID# is " . $new_platform->platform_id;
|
||||
$defaults->{'platform_id'} = $new_platform->platform_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to add platform.";
|
||||
}
|
||||
} elsif ($c->param("edit_platform_form_mode") eq "edit") {
|
||||
my $platform_id = $c->param("edit_platform_form_platform_id");
|
||||
my $platform = Litmus::DB::Platform->retrieve($platform_id);
|
||||
if ($platform) {
|
||||
$platform->name($c->param('edit_platform_form_name'));
|
||||
$platform->iconpath($c->param('edit_platform_form_iconpath') ? $c->param('edit_platform_form_iconpath') : '');
|
||||
$platform->detect_regexp($c->param('edit_platform_form_detect_regexp') ? $c->param('edit_platform_form_detect_regexp') : '');
|
||||
$rv = $platform->update();
|
||||
if ($rv) {
|
||||
my @selected_products = $c->param("edit_platform_form_platform_products");
|
||||
$platform->update_products(\@selected_products);
|
||||
$status = "success";
|
||||
$message = "Platform ID# $platform_id updated successfully.";
|
||||
$defaults->{'platform_id'} = $platform_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to update platform ID# $platform_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Platform ID# $platform_id not found.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Process opsys changes.
|
||||
if ($c->param("delete_opsys_button") and
|
||||
$c->param("opsys_id")) {
|
||||
my $opsys_id = $c->param("opsys_id");
|
||||
my $opsys = Litmus::DB::Opsys->retrieve($opsys_id);
|
||||
if ($opsys) {
|
||||
$rv = $opsys->delete;
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Operating system ID# $opsys_id deleted successfully.";
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to delete Operating system ID# $opsys_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Operating system ID# $opsys_id does not exist. (Already deleted?)";
|
||||
}
|
||||
} elsif ($c->param("edit_opsys_form_mode")) {
|
||||
if ($c->param("edit_opsys_form_mode") eq "add") {
|
||||
my %hash = (
|
||||
name => $c->param('edit_opsys_form_name'),
|
||||
platform_id => $c->param('edit_opsys_form_platform_id'),
|
||||
detect_regexp => $c->param('edit_opsys_form_detect_regexp'),
|
||||
);
|
||||
my $new_opsys =
|
||||
Litmus::DB::Opsys->create(\%hash);
|
||||
if ($new_opsys) {
|
||||
$status = "success";
|
||||
$message = "Operating system added successfully. New operating system ID# is " . $new_opsys->opsys_id;
|
||||
$defaults->{'opsys_id'} = $new_opsys->opsys_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to add operating system.";
|
||||
}
|
||||
} elsif ($c->param("edit_opsys_form_mode") eq "edit") {
|
||||
my $opsys_id = $c->param("edit_opsys_form_opsys_id");
|
||||
my $opsys = Litmus::DB::Opsys->retrieve($opsys_id);
|
||||
if ($opsys) {
|
||||
$opsys->name($c->param('edit_opsys_form_name'));
|
||||
$opsys->platform_id($c->param('edit_opsys_form_platform_id'));
|
||||
$opsys->detect_regexp($c->param('edit_opsys_form_detect_regexp') ? $c->param('edit_opsys_form_detect_regexp') : '');
|
||||
$rv = $opsys->update();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Operating system ID# $opsys_id updated successfully.";
|
||||
$defaults->{'opsys_id'} = $opsys_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to update operating system ID# $opsys_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Operating systen ID# $opsys_id not found.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Process branch changes.
|
||||
if ($c->param("delete_branch_button") and
|
||||
$c->param("branch_id")) {
|
||||
my $branch_id = $c->param("branch_id");
|
||||
my $branch = Litmus::DB::Branch->retrieve($branch_id);
|
||||
if ($branch) {
|
||||
$rv = $branch->delete;
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Branch ID# $branch_id deleted successfully.";
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to delete branch ID# $branch_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Branch ID# $branch_id does not exist. (Already deleted?)";
|
||||
}
|
||||
} elsif ($c->param("edit_branch_form_mode")) {
|
||||
my $enabled = $c->param('edit_branch_form_enabled') ? 1 : 0;
|
||||
if ($c->param("edit_branch_form_mode") eq "add") {
|
||||
my %hash = (
|
||||
name => $c->param('edit_branch_form_name'),
|
||||
product_id => $c->param('edit_branch_form_product_id'),
|
||||
detect_regexp => $c->param('edit_branch_form_detect_regexp'),
|
||||
enabled => $enabled,
|
||||
);
|
||||
my $new_branch =
|
||||
Litmus::DB::Branch->create(\%hash);
|
||||
if ($new_branch) {
|
||||
$status = "success";
|
||||
$message = "Branch added successfully. New branch ID# is " . $new_branch->branch_id;
|
||||
$defaults->{'branch_id'} = $new_branch->branch_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to add branch.";
|
||||
}
|
||||
} elsif ($c->param("edit_branch_form_mode") eq "edit") {
|
||||
my $branch_id = $c->param("edit_branch_form_branch_id");
|
||||
my $branch = Litmus::DB::Branch->retrieve($branch_id);
|
||||
if ($branch) {
|
||||
$branch->name($c->param('edit_branch_form_name'));
|
||||
$branch->product_id($c->param('edit_branch_form_product_id'));
|
||||
$branch->detect_regexp($c->param('edit_branch_form_detect_regexp') ? $c->param('edit_branch_form_detect_regexp') : '');
|
||||
$branch->enabled($enabled);
|
||||
$rv = $branch->update();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Branch ID# $branch_id updated successfully.";
|
||||
$defaults->{'branch_id'} = $branch_id;
|
||||
$rebuild_cache=1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to update branch ID# $branch_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Branch ID# $branch_id not found.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($rebuild_cache) {
|
||||
rebuildCache();
|
||||
}
|
||||
|
||||
my $products = Litmus::FormWidget->getProducts();
|
||||
my $platforms = Litmus::FormWidget->getPlatforms();
|
||||
my $branches = Litmus::FormWidget->getBranches();
|
||||
my $opsyses = Litmus::FormWidget->getOpsyses();
|
||||
|
||||
my $vars = {
|
||||
title => 'Manage Categories',
|
||||
products => $products,
|
||||
platforms => $platforms,
|
||||
branches => $branches,
|
||||
opsyses => $opsyses,
|
||||
};
|
||||
|
||||
if ($status and $message) {
|
||||
$vars->{'onload'} = "toggleMessage('$status','$message');";
|
||||
}
|
||||
|
||||
|
||||
my $cookie = Litmus::Auth::getCookie();
|
||||
$vars->{"defaultemail"} = $cookie;
|
||||
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);
|
||||
|
||||
Litmus->template()->process("admin/edit_categories.tmpl", $vars) ||
|
||||
internalError(Litmus->template()->error());
|
||||
|
||||
#my $elapsed = tv_interval ( $t0 );
|
||||
#printf "<div id='pageload'>Page took %f seconds to load.</div>", $elapsed;
|
||||
|
||||
exit 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
|
||||
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Litmus.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# the Mozilla Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2006
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Chris Cooper <ccooper@deadsquid.com>
|
||||
# Zach Lipton <zach@zachlipton.com>
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
use strict;
|
||||
|
||||
use Litmus;
|
||||
use Litmus::Auth;
|
||||
use Litmus::Cache;
|
||||
use Litmus::Error;
|
||||
use Litmus::FormWidget;
|
||||
use Litmus::Utils;
|
||||
|
||||
use CGI;
|
||||
use Date::Manip;
|
||||
use JSON;
|
||||
|
||||
Litmus->init();
|
||||
my $c = Litmus->cgi();
|
||||
|
||||
my $vars;
|
||||
|
||||
my $subgroup_id;
|
||||
my $message;
|
||||
my $status;
|
||||
my $rv;
|
||||
|
||||
|
||||
if ($c->param("searchSubgroupList")) {
|
||||
print $c->header('text/plain');
|
||||
my $product_id = $c->param("product");
|
||||
my $testgroup_id = $c->param("testgroup");
|
||||
|
||||
my $subgroups;
|
||||
|
||||
if ($testgroup_id) {
|
||||
$subgroups = Litmus::DB::Subgroup->search_ByTestgroup($testgroup_id);
|
||||
} elsif ($product_id) {
|
||||
$subgroups = Litmus::DB::Subgroup->search(product => $product_id);
|
||||
}
|
||||
while (my $sg = $subgroups->next) {
|
||||
print $sg->subgroup_id()."\n";
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
# anyone can use this script for its searching capabilities, but if we
|
||||
# get here, then you need to be an admin:
|
||||
Litmus::Auth::requireAdmin('manage_subgroups.cgi');
|
||||
|
||||
if ($c->param("subgroup_id")) {
|
||||
$subgroup_id = $c->param("subgroup_id");
|
||||
}
|
||||
my $rebuild_cache = 0;
|
||||
my $defaults;
|
||||
if ($c->param("delete_subgroup_button")) {
|
||||
my $subgroup = Litmus::DB::Subgroup->retrieve($subgroup_id);
|
||||
if ($subgroup) {
|
||||
$rv = $subgroup->delete_with_refs();
|
||||
if ($rv) {
|
||||
$status = "success";
|
||||
$message = "Subgroup ID# $subgroup_id deleted successfully.";
|
||||
$rebuild_cache = 1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to delete Subgroup ID# $subgroup_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Subgroup ID# $subgroup_id does not exist. (Already deleted?)";
|
||||
}
|
||||
} elsif ($c->param("clone_subgroup_button")) {
|
||||
my $subgroup = Litmus::DB::Subgroup->retrieve($subgroup_id);
|
||||
my $new_subgroup = $subgroup->clone;
|
||||
if ($new_subgroup) {
|
||||
$status = "success";
|
||||
$message = "Subgroup cloned successfully. New subgroup ID# is " . $new_subgroup->subgroup_id;
|
||||
$defaults->{'subgroup_id'} = $new_subgroup->subgroup_id;
|
||||
$rebuild_cache = 1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to clone Subgroup ID# $subgroup_id.";
|
||||
}
|
||||
} elsif ($c->param("editform_mode")) {
|
||||
requireField('product', $c->param('product'));
|
||||
requireField('testgroup', $c->param('testgroup'));
|
||||
my $enabled = $c->param('editform_enabled') ? 1 : 0;
|
||||
if ($c->param("editform_mode") eq "add") {
|
||||
my %hash = (
|
||||
name => $c->param('editform_name'),
|
||||
product_id => $c->param('product'),
|
||||
enabled => $enabled,
|
||||
);
|
||||
my $new_subgroup =
|
||||
Litmus::DB::Subgroup->create(\%hash);
|
||||
|
||||
if ($new_subgroup) {
|
||||
my @selected_testgroups = $c->param("testgroup");
|
||||
$new_subgroup->update_testgroups(\@selected_testgroups);
|
||||
my @selected_testcases = $c->param("editform_subgroup_testcases");
|
||||
$new_subgroup->update_testcases(\@selected_testcases);
|
||||
$status = "success";
|
||||
$message = "Subgroup added successfully. New subgroup ID# is " . $new_subgroup->subgroup_id;
|
||||
$defaults->{'subgroup_id'} = $new_subgroup->subgroup_id;
|
||||
$rebuild_cache = 1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to add subgroup.";
|
||||
}
|
||||
|
||||
} elsif ($c->param("editform_mode") eq "edit") {
|
||||
requireField('subgroup_id', $c->param("editform_subgroup_id"));
|
||||
$subgroup_id = $c->param("editform_subgroup_id");
|
||||
my $subgroup = Litmus::DB::Subgroup->retrieve($subgroup_id);
|
||||
if ($subgroup) {
|
||||
$subgroup->product_id($c->param('editform_product'));
|
||||
$subgroup->enabled($enabled);
|
||||
$subgroup->name($c->param('editform_name'));
|
||||
$rv = $subgroup->update();
|
||||
if ($rv) {
|
||||
my @selected_testgroups = $c->param("testgroup");
|
||||
$subgroup->update_testgroups(\@selected_testgroups);
|
||||
my @selected_testcases = $c->param("editform_subgroup_testcases");
|
||||
$subgroup->update_testcases(\@selected_testcases);
|
||||
$status = "success";
|
||||
$message = "Subgroup ID# $subgroup_id updated successfully.";
|
||||
$defaults->{'subgroup_id'} = $subgroup_id;
|
||||
$rebuild_cache = 1;
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Failed to update subgroup ID# $subgroup_id.";
|
||||
}
|
||||
} else {
|
||||
$status = "failure";
|
||||
$message = "Subgroup ID# $subgroup_id not found.";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$defaults->{'subgroup_id'} = $c->param("subgroup_id");
|
||||
}
|
||||
|
||||
if ($defaults) {
|
||||
$vars->{'defaults'} = $defaults;
|
||||
}
|
||||
|
||||
if ($status and $message) {
|
||||
$vars->{'onload'} = "toggleMessage('$status','$message');";
|
||||
}
|
||||
|
||||
if ($rebuild_cache) {
|
||||
rebuildCache();
|
||||
}
|
||||
|
||||
my $subgroups = Litmus::FormWidget->getSubgroups;
|
||||
my $products = Litmus::FormWidget->getProducts();
|
||||
my $testcases = Litmus::FormWidget->getTestcases;
|
||||
|
||||
my $json = JSON->new(skipinvalid => 1, convblessed => 1);
|
||||
my $testcases_js = $json->objToJson($testcases);
|
||||
|
||||
$vars->{'title'} = "Manage Subgroups";
|
||||
$vars->{'subgroups'} = $subgroups;
|
||||
$vars->{'products'} = $products;
|
||||
$vars->{'all_testcases'} = $testcases_js;
|
||||
$vars->{'user'} = Litmus::Auth::getCurrentUser();
|
||||
|
||||
my $cookie = Litmus::Auth::getCookie();
|
||||
$vars->{"defaultemail"} = $cookie;
|
||||
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);
|
||||
|
||||
print $c->header();
|
||||
|
||||
Litmus->template()->process("admin/manage_subgroups.tmpl", $vars) ||
|
||||
internalError("Error loading template.");
|
||||
|
||||