9/29/09

Validation of Publish button

I want that publish/unpublish command will be performed only if the survey have at least one question.
If the publish button pressed in the zero -question line - the user must get the red warning message:

You cannot publish survey with zero questions


But how to do it? The validate function cannot receive any additional parameters except
object sender, ServerValidateEventArgs e
And we cant even figure out which repeater item sended the command since the publish button not requires any item checkboxses to be checked.

Custom validator:

<asp:CustomValidator ID="CustomValidator2" runat="server"
ErrorMessage="You cannot publish survey with zero questions"
ValidationGroup="zeroQuestions" Display="Dynamic" OnServerValidate="IsZeroQuestions">
</asp:CustomValidator>

It raises the server-validation function IsZeroQuestions
how can this function "know" what the questions number of line(Repeater Item) whose raised it?



Well, it means i need to change the way the Publish/Unpublish button working:
From now it will work this way:
protected void PublishThisSurvey(object sender, RepeaterCommandEventArgs e)
    {
        RepeaterItem ri = Repeater1.Items[e.Item.ItemIndex];
        CheckBox chkBox = (CheckBox)ri.FindControl("CheckBox1");
        chkBox.Checked = true

        Validate("zeroQuestions");

        if (!Page.IsValid)
        {
        CustomValidator2.ErrorMessage = "Cannot publish this
            survey with zero questions";


            chkBox.Checked = false;
            return;

        }


        HiddenField hf;
        hf = (HiddenField)ri.FindControl("surveyID");
        int surveyID = Convert.ToInt32(hf.Value);

HiddenField isPhf = (HiddenField)ri.FindControl("IsPublished_hf");
        bool bIsPublished = Convert.ToBoolean(isPhf.Value);
      

        //turn upside
        bIsPublished = (bIsPublished) ? false : true;
        clsDataAccess myclass = new clsDataAccess();
        myclass.UpdateSurveyPublished(surveyID, bIsPublished);
        Repeater1.DataBind();
    }



and the trigger will be ItemCommand of Repeater:
<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="SurveysSqlDataSource" OnItemCommand="PublishThisSurvey">                                    

9/28/09

To make publish/upublish buttons



The surveys list is displayed by using the Repeater control:

<asp:Repeater ID="Repeater1" runat="server"
DataSourceID="SurveysSqlDataSource" >
          
<HeaderTemplate>
<table class='adminlist' cellspacing='1'>
<thead>
<tr>
<th style="width:5px">
Id</th>
<th>
Survey</th>
<th>
Description</th>
<th style="width:5px">
Questions</th> 
<th style="width:5px">
  Answerers
</th>                                                                                     
<th style="width:5px">
Published
</th>
 </tr>
</thead>
</HeaderTemplate>
<ItemTemplate>
<tr>
  <td>
<asp:HiddenField ID="surveyID" Value='<%# Eval("survey_id")%>' runat="server" />
<asp:CheckBox ID="CheckBox1" runat="server"  OnCheckedChanged="unCheckOthers"  AutoPostBack="True"/>
<td>
<%# Eval("name")%></td>
<td>
<%# Eval("description")%></td>
  <td align="center">                                   
<%# Eval("QuestionNum")%>                                       
</td>  
<td align="center">                                   
  <%# Eval("answerersNUM")%>                                       
</td>                                     
<td align="center">
                                       
<asp:ImageButton ID="IsPublishedImageButton" runat="server" ImageUrl='<%#setImageButtonImg( Eval( "IsPublished"))%>'  CommandName='<%# setCommandIsPublished( Eval( "IsPublished"))%>'  CommandArgument='<%# Eval("survey_id") %>' OnCommand='PublishThisSurvey' Enabled='<%# IsEnabled(Eval("QuestionNum")) %>'/>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>

To make the publish \ unpublish button i used imageButton control. When  you click it -the survey become published (or unpublished-depends on its status before)


<asp:ImageButton ID="IsPublishedImageButton" runat="server" ImageUrl='<%#setImageButtonImg( Eval( "IsPublished"))%>'  CommandName='<%# setCommandIsPublished( Eval( "IsPublished"))%>'  CommandArgument='<%# Eval("survey_id") %>' OnCommand='PublishThisSurvey' Enabled='<%# IsEnabled(Eval("QuestionNum")) %>'/>


It was hard work
Because i tried to found a way to pass 2 parameters  to PublishThisSurvey
function: 1-the current state(Published or not) and 2-survey Id.

And it very strange but somehow there is only one command argument avaliable . So finally i find out that i can use the "Command name" property for second argument.
May be it is a very clumsy solution...




    protected void PublishThisSurvey(object sender, CommandEventArgs e)
    {
    

        int surveyID = Convert.ToInt32(e.CommandArgument);
        int IsPublished = Convert.ToInt32(e.CommandName);
        bool bIsPublished = Convert.ToBoolean(IsPublished);

        //turn upside
        bIsPublished = (bIsPublished) ? false : true;
        clsDataAccess myclass = new clsDataAccess();
        myclass.UpdateSurveyPublished(surveyID, bIsPublished);
        Repeater1.DataBind();
    }

9/26/09

Query

In this page must be list of all surveys - and (of course ) add/edit/remove buttoms above. With those buttons Administrator be able to add remove and edit each survey details. Besides, there  must be "View Results" link -that will lead to "all_answerers" page and publish/unpublish button -to determine if this survey will be visible to users.




And the whole page must look something like this:


First of all:
to populate the surveys list it is simple select query ,but how to display here also the number of questions and number of users answered? little more complicate...
I devided the query tasks to few substeps and the things started to look little easier:

1. to build view of all surveys with users who answer them:

SELECT DISTINCT U.user_id, S.survey_id
FROM surveys AS S LEFT OUTER JOIN
questions AS Q ON Q.surv_id = S.survey_id LEFT OUTER JOIN
users_answers AS U ON U.question_id = Q.questionID



2. to build view of each survey and it's answerers count:

SELECT  survey_id, COUNT(user_id) AS answerersNUM
FROM   
(SELECT DISTINCT U.user_id, S.survey_id
FROM   surveys AS S LEFT OUTER JOIN
   questions AS Q ON Q.surv_id = S.survey_id LEFT OUTER JOIN
   users_answers AS U ON U.question_id = Q.questionID)
GROUP BY survey_id




3. to buid view of all survey data and count of its questions:

SELECT     surveys.survey_id, surveys.name, surveys.description,
 surveys.IsPublished,
COUNT(Q.questionID) AS QuestionNum
FROM   surveys LEFT OUTER JOIN
questions AS Q ON Q.surv_id = surveys.survey_id
GROUP BY surveys.survey_id, surveys.name,
 surveys.description, surveys.IsPublished







4. to merge the last and the second views:

SELECT     surveys.survey_id, surveys.name, surveys.description,
surveys.IsPublished, COUNT(Q.questionID) AS QuestionNum, SA.answerersNUM
FROM         surveys INNER JOIN
(SELECT     survey_id, COUNT(user_id) AS answerersNUM
FROM  (SELECT DISTINCT U.user_id, S.survey_id
FROM  surveys AS S LEFT OUTER JOIN
questions AS Q ON Q.surv_id = S.survey_id LEFT OUTER JOIN
users_answers AS U ON U.question_id = Q.questionID) AS SHLAV1
GROUP BY survey_id) AS SA ON SA.survey_id =
surveys.survey_id LEFT OUTER JOIN
questions AS Q ON Q.surv_id = SA.survey_id
GROUP BY surveys.survey_id, surveys.name,
surveys.description, surveys.IsPublished, SA.answerersNUM




Well thats it; the query little complicated but its working fine...






Download the SqlScript of creating DB tables link

9/24/09

Storing the questions data in the temporary array

Its obvious that before getting answers we need to store them somethere.
I did it in the beginning of users_Default Page class
//to store all the questions
private static List<cQuestion> Questions=new List<cQuestion>();



cQuestion class have all question data including all the answers(options)

The interesting thing is, that even after clicking button and raising postback event the contents of this listarray are still in memory- the consequence of static definition...

Get the answers...

Today i will write about how i get all the user answers  and store them in users_answers table:


    protected void saveAnswers()
    {
       if (IsPostBack)
       {


           clsDataAccess myclass = new clsDataAccess();
           myclass.openConnection();


           SqlCommand selectCMD = new SqlCommand("SELECT user_id, user_name, question_id, answer_id, answer_text FROM users_answers", myclass.getConn());
           selectCMD.CommandTimeout = 30;

           SqlDataAdapter users_answersDA = new SqlDataAdapter();
           users_answersDA.SelectCommand = selectCMD;

           DataSet users_answersDS = new DataSet();
                                
           users_answersDA.TableMappings.Add("Table", "users_answers");
           users_answersDA.Fill(users_answersDS, "users_answers");
          
           myclass.closeConnection();


           string strUserID = Membership.GetUser(User.Identity.Name).ProviderUserKey.ToString();
           bool IsAnyAnswered = false;


           foreach (cQuestion oQuestion in Questions)
           {
               if (oQuestion.SaveAnswers(Request, users_answersDS.Tables["users_answers"], User.Identity.Name, strUserID))
                   IsAnyAnswered = true;

           }

         

           if (IsAnyAnswered)
           {


               MultiView1.ActiveViewIndex = 1;

           }
       }//end of save answers

I have put on the page the MultiView control to show the user the "Thank you"
After he had answered the questions





Download the soure link

9/23/09

Survey and user ID's

As you remember, after user had chose answers he will press the submit buttom
And his answers will be stored in DataBase.
For being able to store the answers
we need user's 'id' to be stored along with each of his answers.
This code can get us that we need:
string strUserID = Membership.GetUser(User.Identity.Name).ProviderUserKey.ToString();



Getting Relevant Survey Id
Same user cannot do the same survey twice, therefore we need to display him one of the surveys he didnt answer yet.
Here is stored procedure i wrote for this task:
ALTER PROCEDURE dbo.getRelevantSurveys

@user_id nvarchar(256)

AS
SELECT  [survey_id]
FROM    [surveys]
WHERE   [IsPublished]=1 AND
(survey_id NOT IN
(SELECT surveys_1.survey_id
FROM  users_answers INNER JOIN questions ON
[users_answers].[question_id] = [questions].[questionID]
INNER JOIN

surveys AS surveys_1 ON
surveys_1.[survey_id]= questions.surv_id
WHERE      (users_answers.user_id = @user_id)
GROUP BY surveys_1.survey_id))
      RETURN

9/22/09

Displaying all the questions of the survey

In this page we want user to see all the questions of certain survey (and answer them).
I chose to do it in following way:
First :
This Store Procedure gets all the answers with their question aside.

ALTER PROCEDURE dbo.AllQuestionsOfCurrentSurvey
@SurveyID int



AS
            SELECT
            [questions].[QuestionID],
            [questions].[type],
            [answers].[answer],
            [answers].[answerID],
            [questions].[question]             
      FROM [answers] LEFT JOIN [questions]
            ON [answers].[quest_id] = [questions].[questionID]
      WHERE
            ([questions].[surv_id] =@SurveyID)
      RETURN
Now we need to place this data on the page:
The question with answer options aside.



   private void LoadData()
    {
                   
                  StringBuilder sb = new StringBuilder();
                  //string myQuery ="";
                 


                 
                 
                  clsDataAccess myclass = new clsDataAccess();
                  myclass.openConnection();
                 
                 
                 
                  SqlDataReader myReader = myclass.getSurveyData(1);

                  //int mycount=1;
            int questionID = 0;
                  int new_questionID = 0;
            int question_type=0;
            bool firstTime=true;

                  while (myReader.Read() )
                  {


                new_questionID =Convert.ToInt32(myReader["questionID"]);
                question_type=Convert.ToInt32(myReader["type"]);



                if (questionID != new_questionID)//starting new question
                {
                    questionID = new_questionID;


                   
                    if (!firstTime)//to close the prevous table
                    {
                        sb.Append("</table>");
                    }
                    else
                    {
                        firstTime = false;
                    }

                


                    //display quetion:
                    sb.Append("<table width='100%' bgcolor='blue'>");
                    sb.Append("<tr>");
                    sb.Append("<td colspan=2 bgcolor='white'>");
                    sb.Append(myReader["question"].ToString());
                    sb.Append("</td>");
                    sb.Append("</tr>");
                   
                }


                    //dispaly all the answers:


                   //dispaly different inputs for each question type
                   
                    switch(question_type)
                    {
                        case 1: //chose many options answer
                            sb.Append("<tr>");
                            sb.Append("<td bgcolor='white' width='90%' align='left'>");
                            sb.Append(myReader["answer"].ToString());
                            sb.Append("</td>");
                            sb.Append("<td bgcolor='white' align='center'>");
                            sb.Append("<INPUT TYPE=RADIO NAME='q_" + questionID + "'  value='"+Convert.ToInt32(myReader["type"])+"'/>");
                            sb.Append("</td>");
                            sb.Append("</tr>");
                            break;

                        case 2://select one option answer
                            sb.Append("<tr>");
                            sb.Append("<td bgcolor='white' width='90%' align='left'>");
                            sb.Append(myReader["answer"].ToString());
                            sb.Append("</td>");
                            sb.Append("<td bgcolor='white' align='center'>");
                            sb.Append("<input type='checkbox' name='q_" + questionID + "'   />");
                            sb.Append("</td>");
                            sb.Append("</tr>");
                            break;

                        case 3: //text answer
                            sb.Append("<tr>");
                            sb.Append("<td bgcolor='white' width='90%' align='left' colspan=2>");
                            sb.Append("<textarea cols='50' rows='5' name='q_" + questionID + "' ></textarea>");
                            sb.Append("</td>");
                            sb.Append("</tr>");
                            break;
                    }


               
                   

           
            }//end of while

            //care about the last record
            sb.Append(
"</table>");
            Questions.Add(oQuestion);

 

            myReader.Close();
            myclass.closeConnection();

            LtlQuestionForm.Text = sb.ToString();
    }//end of LoadData
}
Note that all the html is sended as text property to Literal control (currently single control on the page)

Finally
Now users (in members role) can view now all the questions of survey (currently survey one) displayed






To dowload the source: link

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...